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/IInputMethodManager.aidl \
|
||||||
core/java/com/android/internal/view/IInputMethodSession.aidl \
|
core/java/com/android/internal/view/IInputMethodSession.aidl \
|
||||||
core/java/com/android/internal/widget/IRemoteViewsFactory.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/ICountryDetector.aidl \
|
||||||
location/java/android/location/ICountryListener.aidl \
|
location/java/android/location/ICountryListener.aidl \
|
||||||
location/java/android/location/IGeocodeProvider.aidl \
|
location/java/android/location/IGeocodeProvider.aidl \
|
||||||
|
28
api/11.xml
28
api/11.xml
@ -188,6 +188,17 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</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"
|
<field name="BIND_WALLPAPER"
|
||||||
type="java.lang.String"
|
type="java.lang.String"
|
||||||
transient="false"
|
transient="false"
|
||||||
@ -252332,6 +252343,23 @@
|
|||||||
<parameter name="intent" type="android.content.Intent">
|
<parameter name="intent" type="android.content.Intent">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</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"
|
<method name="setScrollPosition"
|
||||||
return="void"
|
return="void"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
|
@ -188,6 +188,17 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</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"
|
<field name="BIND_WALLPAPER"
|
||||||
type="java.lang.String"
|
type="java.lang.String"
|
||||||
transient="false"
|
transient="false"
|
||||||
@ -253918,6 +253929,23 @@
|
|||||||
<parameter name="intent" type="android.content.Intent">
|
<parameter name="intent" type="android.content.Intent">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</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"
|
<method name="setScrollPosition"
|
||||||
return="void"
|
return="void"
|
||||||
abstract="false"
|
abstract="false"
|
||||||
@ -261711,7 +261739,7 @@
|
|||||||
deprecated="not deprecated"
|
deprecated="not deprecated"
|
||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
<parameter name="arg0" type="T">
|
<parameter name="t" type="T">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</method>
|
||||||
</interface>
|
</interface>
|
||||||
|
@ -18,6 +18,7 @@ package android.appwidget;
|
|||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
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
|
* Get the list of appWidgetIds that have been bound to the given AppWidget
|
||||||
* provider.
|
* provider.
|
||||||
|
@ -58,6 +58,12 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
|
|
||||||
private static final String LOG_TAG = "RemoteViews";
|
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
|
* The package name of the package containing the layout
|
||||||
* resource. (Added to the parcel)
|
* resource. (Added to the parcel)
|
||||||
@ -1276,6 +1282,22 @@ public class RemoteViews implements Parcelable, Filter {
|
|||||||
* providing data to the RemoteViewsAdapter
|
* providing data to the RemoteViewsAdapter
|
||||||
*/
|
*/
|
||||||
public void setRemoteAdapter(int viewId, Intent intent) {
|
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);
|
setIntent(viewId, "setRemoteViewsAdapter", intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,20 +22,21 @@ import java.util.HashSet;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.appwidget.AppWidgetManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.View.MeasureSpec;
|
import android.view.View.MeasureSpec;
|
||||||
|
|
||||||
|
import com.android.internal.widget.IRemoteViewsAdapterConnection;
|
||||||
import com.android.internal.widget.IRemoteViewsFactory;
|
import com.android.internal.widget.IRemoteViewsFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -43,11 +44,22 @@ import com.android.internal.widget.IRemoteViewsFactory;
|
|||||||
* to be later inflated as child views.
|
* to be later inflated as child views.
|
||||||
*/
|
*/
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public class RemoteViewsAdapter extends BaseAdapter {
|
public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback {
|
||||||
private static final String TAG = "RemoteViewsAdapter";
|
private static final String TAG = "RemoteViewsAdapter";
|
||||||
|
|
||||||
private Context mContext;
|
// The max number of items in the cache
|
||||||
private Intent mIntent;
|
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 LayoutInflater mLayoutInflater;
|
||||||
private RemoteViewsAdapterServiceConnection mServiceConnection;
|
private RemoteViewsAdapterServiceConnection mServiceConnection;
|
||||||
private WeakReference<RemoteAdapterConnectionCallback> mCallback;
|
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
|
* garbage collected, and would cause us to leak activities due to the caching mechanism for
|
||||||
* FrameLayouts in the adapter).
|
* FrameLayouts in the adapter).
|
||||||
*/
|
*/
|
||||||
private static class RemoteViewsAdapterServiceConnection implements ServiceConnection {
|
private static class RemoteViewsAdapterServiceConnection extends
|
||||||
|
IRemoteViewsAdapterConnection.Stub {
|
||||||
private boolean mConnected;
|
private boolean mConnected;
|
||||||
private WeakReference<RemoteViewsAdapter> mAdapter;
|
private WeakReference<RemoteViewsAdapter> mAdapter;
|
||||||
private IRemoteViewsFactory mRemoteViewsFactory;
|
private IRemoteViewsFactory mRemoteViewsFactory;
|
||||||
@ -88,8 +101,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
|
mAdapter = new WeakReference<RemoteViewsAdapter>(adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName name,
|
public void onServiceConnected(IBinder service) {
|
||||||
IBinder service) {
|
|
||||||
mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
|
mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service);
|
||||||
mConnected = true;
|
mConnected = true;
|
||||||
|
|
||||||
@ -137,7 +149,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
public void onServiceDisconnected() {
|
||||||
mConnected = false;
|
mConnected = false;
|
||||||
mRemoteViewsFactory = null;
|
mRemoteViewsFactory = null;
|
||||||
|
|
||||||
@ -145,8 +157,9 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
if (adapter == null) return;
|
if (adapter == null) return;
|
||||||
|
|
||||||
// Clear the main/worker queues
|
// Clear the main/worker queues
|
||||||
adapter.mMainQueue.removeMessages(0);
|
adapter.mMainQueue.removeMessages(sUnbindServiceMessageType);
|
||||||
adapter.mWorkerQueue.removeMessages(0);
|
adapter.mMainQueue.removeMessages(sDefaultMessageType);
|
||||||
|
adapter.mWorkerQueue.removeMessages(sDefaultMessageType);
|
||||||
|
|
||||||
final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
|
final RemoteAdapterConnectionCallback callback = adapter.mCallback.get();
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
@ -574,20 +587,26 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
|
public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mIntent = intent;
|
mIntent = intent;
|
||||||
|
mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
|
||||||
mLayoutInflater = LayoutInflater.from(context);
|
mLayoutInflater = LayoutInflater.from(context);
|
||||||
if (mIntent == null) {
|
if (mIntent == null) {
|
||||||
throw new IllegalArgumentException("Non-null Intent must be specified.");
|
throw new IllegalArgumentException("Non-null Intent must be specified.");
|
||||||
}
|
}
|
||||||
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
|
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 = new HandlerThread("RemoteViewsCache-loader");
|
||||||
mWorkerThread.start();
|
mWorkerThread.start();
|
||||||
mWorkerQueue = new Handler(mWorkerThread.getLooper());
|
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
|
// Initialize the cache and the service connection on startup
|
||||||
mCache = new FixedSizeRemoteViewsCache(50);
|
mCache = new FixedSizeRemoteViewsCache(sDefaultCacheSize);
|
||||||
mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
|
mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback);
|
||||||
mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
|
mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
|
||||||
requestBindService();
|
requestBindService();
|
||||||
@ -687,6 +706,7 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
|
mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId);
|
||||||
|
enqueueDeferredUnbindServiceMessage();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -879,10 +899,34 @@ public class RemoteViewsAdapter extends BaseAdapter {
|
|||||||
super.notifyDataSetChanged();
|
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() {
|
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()) {
|
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();
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -17,8 +17,10 @@
|
|||||||
package com.android.internal.appwidget;
|
package com.android.internal.appwidget;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
import android.appwidget.AppWidgetProviderInfo;
|
import android.appwidget.AppWidgetProviderInfo;
|
||||||
import com.android.internal.appwidget.IAppWidgetHost;
|
import com.android.internal.appwidget.IAppWidgetHost;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
@ -46,6 +48,8 @@ interface IAppWidgetService {
|
|||||||
List<AppWidgetProviderInfo> getInstalledProviders();
|
List<AppWidgetProviderInfo> getInstalledProviders();
|
||||||
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
|
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
|
||||||
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
|
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);
|
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:description="@string/permdesc_backup"
|
||||||
android:protectionLevel="signatureOrSystem" />
|
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
|
<!-- Allows an application to tell the AppWidget service which application
|
||||||
can access AppWidget's data. The normal user flow is that a user
|
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
|
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
|
<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>
|
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. -->
|
<!-- 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>
|
<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. -->
|
<!-- 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;
|
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.AlarmManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.appwidget.AppWidgetManager;
|
import android.appwidget.AppWidgetManager;
|
||||||
@ -24,46 +41,37 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.Intent.FilterComparison;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.content.pm.ServiceInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.content.res.XmlResourceParser;
|
import android.content.res.XmlResourceParser;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Pair;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
import android.widget.RemoteViews;
|
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.IAppWidgetHost;
|
||||||
|
import com.android.internal.appwidget.IAppWidgetService;
|
||||||
import com.android.internal.util.FastXmlSerializer;
|
import com.android.internal.util.FastXmlSerializer;
|
||||||
|
import com.android.internal.widget.IRemoteViewsAdapterConnection;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
|
||||||
|
|
||||||
class AppWidgetService extends IAppWidgetService.Stub
|
class AppWidgetService extends IAppWidgetService.Stub
|
||||||
{
|
{
|
||||||
@ -107,6 +115,56 @@ class AppWidgetService extends IAppWidgetService.Stub
|
|||||||
Host host;
|
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;
|
Context mContext;
|
||||||
Locale mLocale;
|
Locale mLocale;
|
||||||
PackageManager mPackageManager;
|
PackageManager mPackageManager;
|
||||||
@ -294,6 +352,9 @@ class AppWidgetService extends IAppWidgetService.Stub
|
|||||||
}
|
}
|
||||||
|
|
||||||
void deleteAppWidgetLocked(AppWidgetId id) {
|
void deleteAppWidgetLocked(AppWidgetId id) {
|
||||||
|
// We first unbind all services that are bound to this id
|
||||||
|
unbindAppWidgetRemoteViewsServicesLocked(id);
|
||||||
|
|
||||||
Host host = id.host;
|
Host host = id.host;
|
||||||
host.instances.remove(id);
|
host.instances.remove(id);
|
||||||
pruneHostLocked(host);
|
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) {
|
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
|
||||||
synchronized (mAppWidgetIds) {
|
synchronized (mAppWidgetIds) {
|
||||||
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
|
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
|
||||||
|
Reference in New Issue
Block a user