summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Jurka <mikejurka@google.com>2012-04-24 19:06:14 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-04-24 19:06:14 -0700
commit2c8e19e81fb930fbb4d353c91ca0852fe91d8166 (patch)
treef705b6da91491542843042b2db0f814b62324c85
parent38cb84fd4b39c08cbd5b65b7383e712b9f476e3f (diff)
parent61a5b0160d9f2e53ef4d4b451212a63032dad32d (diff)
downloadframeworks_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.txt1
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java115
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl4
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--services/java/com/android/server/AppWidgetService.java17
-rw-r--r--services/java/com/android/server/AppWidgetServiceImpl.java83
7 files changed, 221 insertions, 10 deletions
diff --git a/api/16.txt b/api/16.txt
index 9e9f880..a04faf0 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -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);