diff options
author | Michael Jurka <mikejurka@google.com> | 2012-04-24 19:06:14 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-04-24 19:06:14 -0700 |
commit | 2c8e19e81fb930fbb4d353c91ca0852fe91d8166 (patch) | |
tree | f705b6da91491542843042b2db0f814b62324c85 | |
parent | 38cb84fd4b39c08cbd5b65b7383e712b9f476e3f (diff) | |
parent | 61a5b0160d9f2e53ef4d4b451212a63032dad32d (diff) | |
download | frameworks_base-2c8e19e81fb930fbb4d353c91ca0852fe91d8166.zip frameworks_base-2c8e19e81fb930fbb4d353c91ca0852fe91d8166.tar.gz frameworks_base-2c8e19e81fb930fbb4d353c91ca0852fe91d8166.tar.bz2 |
Merge "New API to allow third-party apps to bind widgets"
-rw-r--r-- | api/16.txt | 1 | ||||
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | core/java/android/appwidget/AppWidgetManager.java | 115 | ||||
-rw-r--r-- | core/java/com/android/internal/appwidget/IAppWidgetService.aidl | 4 | ||||
-rw-r--r-- | core/res/AndroidManifest.xml | 7 | ||||
-rw-r--r-- | services/java/com/android/server/AppWidgetService.java | 17 | ||||
-rw-r--r-- | services/java/com/android/server/AppWidgetServiceImpl.java | 83 |
7 files changed, 221 insertions, 10 deletions
@@ -4151,7 +4151,6 @@ package android.appwidget { } public class AppWidgetManager { - method public void bindAppWidgetId(int, android.content.ComponentName); method public int[] getAppWidgetIds(android.content.ComponentName); method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int); method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders(); diff --git a/api/current.txt b/api/current.txt index 4f7bc31..408d044 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4313,7 +4313,7 @@ package android.appwidget { } public class AppWidgetManager { - method public void bindAppWidgetId(int, android.content.ComponentName); + method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName); method public android.os.Bundle getAppWidgetExtras(int); method public int[] getAppWidgetIds(android.content.ComponentName); method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int); @@ -4327,6 +4327,7 @@ package android.appwidget { method public void updateAppWidget(int, android.widget.RemoteViews); method public void updateAppWidget(android.content.ComponentName, android.widget.RemoteViews); method public void updateAppWidgetExtras(int, android.os.Bundle); + field public static final java.lang.String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND"; field public static final java.lang.String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE"; field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED"; field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED"; @@ -4341,6 +4342,7 @@ package android.appwidget { field public static final java.lang.String EXTRA_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth"; field public static final java.lang.String EXTRA_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight"; field public static final java.lang.String EXTRA_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth"; + field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider"; field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras"; field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo"; field public static final int INVALID_APPWIDGET_ID = 0; // 0x0 diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 83ab817..f2e909e 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -80,6 +80,46 @@ public class AppWidgetManager { public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK"; /** + * Send this from your {@link AppWidgetHost} activity when you want to bind an AppWidget to + * display and bindAppWidgetIdIfAllowed returns false. + * <p> + * You must supply the following extras: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_ID}</td> + * <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider + * you provide.</td> + * </tr> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_PROVIDER}</td> + * <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget. + * </td> + * </tr> + * </table> + * + * <p> + * The system will respond with an onActivityResult call with the following extras in + * the intent: + * <table> + * <tr> + * <td>{@link #EXTRA_APPWIDGET_ID}</td> + * <td>The appWidgetId that you supplied in the original intent.</td> + * </tr> + * </table> + * <p> + * When you receive the result from the AppWidget bind activity, if the resultCode is + * {@link android.app.Activity#RESULT_OK}, the AppWidget has been bound. You should then + * check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its + * configuration activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you + * should delete + * the appWidgetId. + * + * @see #ACTION_APPWIDGET_CONFIGURE + * + */ + public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND"; + + /** * Sent when it is time to configure your AppWidget while it is being added to a host. * This action is not sent as a broadcast to the AppWidget provider, but as a startActivity * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo meta-data}. @@ -144,6 +184,13 @@ public class AppWidgetManager { public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds"; /** + * An intent extra that contains the component name of a AppWidget provider. + * <p> + * The value will be an ComponentName. + */ + public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider"; + + /** * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of * {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are * installed. (This is how the launcher shows the search widget). @@ -501,12 +548,14 @@ public class AppWidgetManager { /** * Set the component for a given appWidgetId. * - * <p class="note">You need the APPWIDGET_LIST permission. This method is to be used by the - * AppWidget picker. + * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding + * widgets always for your component. This method is used by the AppWidget picker and + * should not be used by other apps. * * @param appWidgetId The AppWidget instance for which to set the RemoteViews. * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget * provider for this AppWidget. + * @hide */ public void bindAppWidgetId(int appWidgetId, ComponentName provider) { try { @@ -518,6 +567,68 @@ public class AppWidgetManager { } /** + * Set the component for a given appWidgetId. + * + * <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding + * widgets always for your component. Should be used by apps that host widgets; if this + * method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to + * bind + * + * @param appWidgetId The AppWidget instance for which to set the RemoteViews. + * @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget + * provider for this AppWidget. + * @return true if this component has permission to bind the AppWidget + */ + public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider) { + if (mContext == null) { + return false; + } + try { + return sService.bindAppWidgetIdIfAllowed( + mContext.getPackageName(), appWidgetId, provider); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Query if a given package was granted permission by the user to bind app widgets + * + * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission + * + * @param packageName The package for which the permission is being queried + * @return true if the package was granted permission by the user to bind app widgets + * @hide + */ + public boolean hasBindAppWidgetPermission(String packageName) { + try { + return sService.hasBindAppWidgetPermission(packageName); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Changes any user-granted permission for the given package to bind app widgets + * + * <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission + * + * @param provider The package whose permission is being changed + * @param permission Whether to give the package permission to bind widgets + * @hide + */ + public void setBindAppWidgetPermission(String packageName, boolean permission) { + try { + sService.setBindAppWidgetPermission(packageName, permission); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** * Binds the RemoteViewsService for a given appWidgetId and intent. * * The appWidgetId specified must already be bound to the calling AppWidgetHost via diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index b1b57e7..327fe07 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -50,7 +50,11 @@ interface IAppWidgetService { void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId); List<AppWidgetProviderInfo> getInstalledProviders(); AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId); + boolean hasBindAppWidgetPermission(in String packageName); + void setBindAppWidgetPermission(in String packageName, in boolean permission); void bindAppWidgetId(int appWidgetId, in ComponentName provider); + boolean bindAppWidgetIdIfAllowed( + in String packageName, 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); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 25d3762..e1b9d55 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1480,6 +1480,13 @@ android:description="@string/permdesc_bindGadget" android:protectionLevel="signature|system" /> + <!-- Internal permission allowing an application to query/set which + applications can bind AppWidgets. + @hide --> + <permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="signature|system" /> + <!-- Allows applications to change the background data setting @hide pending API council --> <permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING" diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index bf958a5..7e71b08 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -208,6 +208,23 @@ class AppWidgetService extends IAppWidgetService.Stub } @Override + public boolean bindAppWidgetIdIfAllowed( + String packageName, int appWidgetId, ComponentName provider) throws RemoteException { + return getImplForUser().bindAppWidgetIdIfAllowed(packageName, appWidgetId, provider); + } + + @Override + public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException { + return getImplForUser().hasBindAppWidgetPermission(packageName); + } + + @Override + public void setBindAppWidgetPermission(String packageName, boolean permission) + throws RemoteException { + getImplForUser().setBindAppWidgetPermission(packageName, permission); + } + + @Override public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) throws RemoteException { getImplForUser().bindRemoteViewsService(appWidgetId, intent, connection); diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index 3b43b9b..a0b8c531 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -24,8 +24,8 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.Intent.FilterComparison; +import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -56,7 +56,6 @@ import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; -import com.android.server.am.ActivityManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -167,6 +166,8 @@ class AppWidgetServiceImpl { int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>(); ArrayList<Host> mHosts = new ArrayList<Host>(); + // set of package names + HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>(); boolean mSafeMode; int mUserId; boolean mStateLoaded; @@ -493,10 +494,7 @@ class AppWidgetServiceImpl { } } - public void bindAppWidgetId(int appWidgetId, ComponentName provider) { - mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, - "bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider); - + private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) { final long ident = Binder.clearCallingIdentity(); try { synchronized (mAppWidgetIds) { @@ -541,6 +539,67 @@ class AppWidgetServiceImpl { } } + public void bindAppWidgetId(int appWidgetId, ComponentName provider) { + mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, + "bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider); + bindAppWidgetIdImpl(appWidgetId, provider); + } + + public boolean bindAppWidgetIdIfAllowed( + String packageName, int appWidgetId, ComponentName provider) { + try { + mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null); + } catch (SecurityException se) { + if (!callerHasBindAppWidgetPermission(packageName)) { + return false; + } + } + bindAppWidgetIdImpl(appWidgetId, provider); + return true; + } + + private boolean callerHasBindAppWidgetPermission(String packageName) { + int callingUid = Binder.getCallingUid(); + try { + if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) { + return false; + } + } catch (Exception e) { + return false; + } + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + return mPackagesWithBindWidgetPermission.contains(packageName); + } + } + + public boolean hasBindAppWidgetPermission(String packageName) { + mContext.enforceCallingPermission( + android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS, + "hasBindAppWidgetPermission packageName=" + packageName); + + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + return mPackagesWithBindWidgetPermission.contains(packageName); + } + } + + public void setBindAppWidgetPermission(String packageName, boolean permission) { + mContext.enforceCallingPermission( + android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS, + "setBindAppWidgetPermission packageName=" + packageName); + + synchronized (mAppWidgetIds) { + ensureStateLoadedLocked(); + if (permission) { + mPackagesWithBindWidgetPermission.add(packageName); + } else { + mPackagesWithBindWidgetPermission.remove(packageName); + } + } + saveStateLocked(); + } + // Binds to a specific RemoteViewsService public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) { synchronized (mAppWidgetIds) { @@ -1375,6 +1434,13 @@ class AppWidgetServiceImpl { out.endTag(null, "g"); } + Iterator<String> it = mPackagesWithBindWidgetPermission.iterator(); + while (it.hasNext()) { + out.startTag(null, "b"); + out.attribute(null, "packageName", it.next()); + out.endTag(null, "b"); + } + out.endTag(null, "gs"); out.endDocument(); @@ -1445,6 +1511,11 @@ class AppWidgetServiceImpl { .parseInt(parser.getAttributeValue(null, "id"), 16); mHosts.add(host); } + } else if ("b".equals(tag)) { + String packageName = parser.getAttributeValue(null, "packageName"); + if (packageName != null) { + mPackagesWithBindWidgetPermission.add(packageName); + } } else if ("g".equals(tag)) { AppWidgetId id = new AppWidgetId(); id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16); |