Skip to content

Commit 2c8e19e

Browse files
mikejurkaAndroid (Google) Code Review
authored andcommitted
Merge "New API to allow third-party apps to bind widgets"
2 parents 38cb84f + 61a5b01 commit 2c8e19e

File tree

7 files changed

+221
-10
lines changed

7 files changed

+221
-10
lines changed

api/16.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4151,7 +4151,6 @@ package android.appwidget {
41514151
}
41524152

41534153
public class AppWidgetManager {
4154-
method public void bindAppWidgetId(int, android.content.ComponentName);
41554154
method public int[] getAppWidgetIds(android.content.ComponentName);
41564155
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
41574156
method public java.util.List<android.appwidget.AppWidgetProviderInfo> getInstalledProviders();

api/current.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4313,7 +4313,7 @@ package android.appwidget {
43134313
}
43144314

43154315
public class AppWidgetManager {
4316-
method public void bindAppWidgetId(int, android.content.ComponentName);
4316+
method public boolean bindAppWidgetIdIfAllowed(int, android.content.ComponentName);
43174317
method public android.os.Bundle getAppWidgetExtras(int);
43184318
method public int[] getAppWidgetIds(android.content.ComponentName);
43194319
method public android.appwidget.AppWidgetProviderInfo getAppWidgetInfo(int);
@@ -4327,6 +4327,7 @@ package android.appwidget {
43274327
method public void updateAppWidget(int, android.widget.RemoteViews);
43284328
method public void updateAppWidget(android.content.ComponentName, android.widget.RemoteViews);
43294329
method public void updateAppWidgetExtras(int, android.os.Bundle);
4330+
field public static final java.lang.String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
43304331
field public static final java.lang.String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
43314332
field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
43324333
field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -4341,6 +4342,7 @@ package android.appwidget {
43414342
field public static final java.lang.String EXTRA_APPWIDGET_MAX_WIDTH = "appWidgetMaxWidth";
43424343
field public static final java.lang.String EXTRA_APPWIDGET_MIN_HEIGHT = "appWidgetMinHeight";
43434344
field public static final java.lang.String EXTRA_APPWIDGET_MIN_WIDTH = "appWidgetMinWidth";
4345+
field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
43444346
field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
43454347
field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
43464348
field public static final int INVALID_APPWIDGET_ID = 0; // 0x0

core/java/android/appwidget/AppWidgetManager.java

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,46 @@ public class AppWidgetManager {
7979
*/
8080
public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
8181

82+
/**
83+
* Send this from your {@link AppWidgetHost} activity when you want to bind an AppWidget to
84+
* display and bindAppWidgetIdIfAllowed returns false.
85+
* <p>
86+
* You must supply the following extras:
87+
* <table>
88+
* <tr>
89+
* <td>{@link #EXTRA_APPWIDGET_ID}</td>
90+
* <td>A newly allocated appWidgetId, which will be bound to the AppWidget provider
91+
* you provide.</td>
92+
* </tr>
93+
* <tr>
94+
* <td>{@link #EXTRA_APPWIDGET_PROVIDER}</td>
95+
* <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget.
96+
* </td>
97+
* </tr>
98+
* </table>
99+
*
100+
* <p>
101+
* The system will respond with an onActivityResult call with the following extras in
102+
* the intent:
103+
* <table>
104+
* <tr>
105+
* <td>{@link #EXTRA_APPWIDGET_ID}</td>
106+
* <td>The appWidgetId that you supplied in the original intent.</td>
107+
* </tr>
108+
* </table>
109+
* <p>
110+
* When you receive the result from the AppWidget bind activity, if the resultCode is
111+
* {@link android.app.Activity#RESULT_OK}, the AppWidget has been bound. You should then
112+
* check the AppWidgetProviderInfo for the returned AppWidget, and if it has one, launch its
113+
* configuration activity. If {@link android.app.Activity#RESULT_CANCELED} is returned, you
114+
* should delete
115+
* the appWidgetId.
116+
*
117+
* @see #ACTION_APPWIDGET_CONFIGURE
118+
*
119+
*/
120+
public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
121+
82122
/**
83123
* Sent when it is time to configure your AppWidget while it is being added to a host.
84124
* This action is not sent as a broadcast to the AppWidget provider, but as a startActivity
@@ -143,6 +183,13 @@ public class AppWidgetManager {
143183
*/
144184
public static final String EXTRA_APPWIDGET_IDS = "appWidgetIds";
145185

186+
/**
187+
* An intent extra that contains the component name of a AppWidget provider.
188+
* <p>
189+
* The value will be an ComponentName.
190+
*/
191+
public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
192+
146193
/**
147194
* An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
148195
* {@link AppWidgetProviderInfo} objects to mix in to the list of AppWidgets that are
@@ -501,12 +548,14 @@ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
501548
/**
502549
* Set the component for a given appWidgetId.
503550
*
504-
* <p class="note">You need the APPWIDGET_LIST permission. This method is to be used by the
505-
* AppWidget picker.
551+
* <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
552+
* widgets always for your component. This method is used by the AppWidget picker and
553+
* should not be used by other apps.
506554
*
507555
* @param appWidgetId The AppWidget instance for which to set the RemoteViews.
508556
* @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget
509557
* provider for this AppWidget.
558+
* @hide
510559
*/
511560
public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
512561
try {
@@ -517,6 +566,68 @@ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
517566
}
518567
}
519568

569+
/**
570+
* Set the component for a given appWidgetId.
571+
*
572+
* <p class="note">You need the BIND_APPWIDGET permission or the user must have enabled binding
573+
* widgets always for your component. Should be used by apps that host widgets; if this
574+
* method returns false, call {@link #ACTION_APPWIDGET_BIND} to request permission to
575+
* bind
576+
*
577+
* @param appWidgetId The AppWidget instance for which to set the RemoteViews.
578+
* @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget
579+
* provider for this AppWidget.
580+
* @return true if this component has permission to bind the AppWidget
581+
*/
582+
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider) {
583+
if (mContext == null) {
584+
return false;
585+
}
586+
try {
587+
return sService.bindAppWidgetIdIfAllowed(
588+
mContext.getPackageName(), appWidgetId, provider);
589+
}
590+
catch (RemoteException e) {
591+
throw new RuntimeException("system server dead?", e);
592+
}
593+
}
594+
595+
/**
596+
* Query if a given package was granted permission by the user to bind app widgets
597+
*
598+
* <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
599+
*
600+
* @param packageName The package for which the permission is being queried
601+
* @return true if the package was granted permission by the user to bind app widgets
602+
* @hide
603+
*/
604+
public boolean hasBindAppWidgetPermission(String packageName) {
605+
try {
606+
return sService.hasBindAppWidgetPermission(packageName);
607+
}
608+
catch (RemoteException e) {
609+
throw new RuntimeException("system server dead?", e);
610+
}
611+
}
612+
613+
/**
614+
* Changes any user-granted permission for the given package to bind app widgets
615+
*
616+
* <p class="note">You need the MODIFY_APPWIDGET_BIND_PERMISSIONS permission
617+
*
618+
* @param provider The package whose permission is being changed
619+
* @param permission Whether to give the package permission to bind widgets
620+
* @hide
621+
*/
622+
public void setBindAppWidgetPermission(String packageName, boolean permission) {
623+
try {
624+
sService.setBindAppWidgetPermission(packageName, permission);
625+
}
626+
catch (RemoteException e) {
627+
throw new RuntimeException("system server dead?", e);
628+
}
629+
}
630+
520631
/**
521632
* Binds the RemoteViewsService for a given appWidgetId and intent.
522633
*

core/java/com/android/internal/appwidget/IAppWidgetService.aidl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ interface IAppWidgetService {
5050
void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
5151
List<AppWidgetProviderInfo> getInstalledProviders();
5252
AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
53+
boolean hasBindAppWidgetPermission(in String packageName);
54+
void setBindAppWidgetPermission(in String packageName, in boolean permission);
5355
void bindAppWidgetId(int appWidgetId, in ComponentName provider);
56+
boolean bindAppWidgetIdIfAllowed(
57+
in String packageName, int appWidgetId, in ComponentName provider);
5458
void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection);
5559
void unbindRemoteViewsService(int appWidgetId, in Intent intent);
5660
int[] getAppWidgetIds(in ComponentName provider);

core/res/AndroidManifest.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,13 @@
14801480
android:description="@string/permdesc_bindGadget"
14811481
android:protectionLevel="signature|system" />
14821482

1483+
<!-- Internal permission allowing an application to query/set which
1484+
applications can bind AppWidgets.
1485+
@hide -->
1486+
<permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
1487+
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
1488+
android:protectionLevel="signature|system" />
1489+
14831490
<!-- Allows applications to change the background data setting
14841491
@hide pending API council -->
14851492
<permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"

services/java/com/android/server/AppWidgetService.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,23 @@ public void bindAppWidgetId(int appWidgetId, ComponentName provider) throws Remo
207207
getImplForUser().bindAppWidgetId(appWidgetId, provider);
208208
}
209209

210+
@Override
211+
public boolean bindAppWidgetIdIfAllowed(
212+
String packageName, int appWidgetId, ComponentName provider) throws RemoteException {
213+
return getImplForUser().bindAppWidgetIdIfAllowed(packageName, appWidgetId, provider);
214+
}
215+
216+
@Override
217+
public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
218+
return getImplForUser().hasBindAppWidgetPermission(packageName);
219+
}
220+
221+
@Override
222+
public void setBindAppWidgetPermission(String packageName, boolean permission)
223+
throws RemoteException {
224+
getImplForUser().setBindAppWidgetPermission(packageName, permission);
225+
}
226+
210227
@Override
211228
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection)
212229
throws RemoteException {

services/java/com/android/server/AppWidgetServiceImpl.java

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import android.content.ComponentName;
2525
import android.content.Context;
2626
import android.content.Intent;
27-
import android.content.ServiceConnection;
2827
import android.content.Intent.FilterComparison;
28+
import android.content.ServiceConnection;
2929
import android.content.pm.ActivityInfo;
3030
import android.content.pm.ApplicationInfo;
3131
import android.content.pm.IPackageManager;
@@ -56,7 +56,6 @@
5656
import com.android.internal.util.FastXmlSerializer;
5757
import com.android.internal.widget.IRemoteViewsAdapterConnection;
5858
import com.android.internal.widget.IRemoteViewsFactory;
59-
import com.android.server.am.ActivityManagerService;
6059

6160
import org.xmlpull.v1.XmlPullParser;
6261
import org.xmlpull.v1.XmlPullParserException;
@@ -167,6 +166,8 @@ public void disconnect() {
167166
int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
168167
final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
169168
ArrayList<Host> mHosts = new ArrayList<Host>();
169+
// set of package names
170+
HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
170171
boolean mSafeMode;
171172
int mUserId;
172173
boolean mStateLoaded;
@@ -493,10 +494,7 @@ void cancelBroadcasts(Provider p) {
493494
}
494495
}
495496

496-
public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
497-
mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
498-
"bindGagetId appWidgetId=" + appWidgetId + " provider=" + provider);
499-
497+
private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider) {
500498
final long ident = Binder.clearCallingIdentity();
501499
try {
502500
synchronized (mAppWidgetIds) {
@@ -541,6 +539,67 @@ public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
541539
}
542540
}
543541

542+
public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
543+
mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET,
544+
"bindAppWidgetId appWidgetId=" + appWidgetId + " provider=" + provider);
545+
bindAppWidgetIdImpl(appWidgetId, provider);
546+
}
547+
548+
public boolean bindAppWidgetIdIfAllowed(
549+
String packageName, int appWidgetId, ComponentName provider) {
550+
try {
551+
mContext.enforceCallingPermission(android.Manifest.permission.BIND_APPWIDGET, null);
552+
} catch (SecurityException se) {
553+
if (!callerHasBindAppWidgetPermission(packageName)) {
554+
return false;
555+
}
556+
}
557+
bindAppWidgetIdImpl(appWidgetId, provider);
558+
return true;
559+
}
560+
561+
private boolean callerHasBindAppWidgetPermission(String packageName) {
562+
int callingUid = Binder.getCallingUid();
563+
try {
564+
if (!UserId.isSameApp(callingUid, getUidForPackage(packageName))) {
565+
return false;
566+
}
567+
} catch (Exception e) {
568+
return false;
569+
}
570+
synchronized (mAppWidgetIds) {
571+
ensureStateLoadedLocked();
572+
return mPackagesWithBindWidgetPermission.contains(packageName);
573+
}
574+
}
575+
576+
public boolean hasBindAppWidgetPermission(String packageName) {
577+
mContext.enforceCallingPermission(
578+
android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
579+
"hasBindAppWidgetPermission packageName=" + packageName);
580+
581+
synchronized (mAppWidgetIds) {
582+
ensureStateLoadedLocked();
583+
return mPackagesWithBindWidgetPermission.contains(packageName);
584+
}
585+
}
586+
587+
public void setBindAppWidgetPermission(String packageName, boolean permission) {
588+
mContext.enforceCallingPermission(
589+
android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
590+
"setBindAppWidgetPermission packageName=" + packageName);
591+
592+
synchronized (mAppWidgetIds) {
593+
ensureStateLoadedLocked();
594+
if (permission) {
595+
mPackagesWithBindWidgetPermission.add(packageName);
596+
} else {
597+
mPackagesWithBindWidgetPermission.remove(packageName);
598+
}
599+
}
600+
saveStateLocked();
601+
}
602+
544603
// Binds to a specific RemoteViewsService
545604
public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
546605
synchronized (mAppWidgetIds) {
@@ -1375,6 +1434,13 @@ boolean writeStateToFileLocked(FileOutputStream stream) {
13751434
out.endTag(null, "g");
13761435
}
13771436

1437+
Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
1438+
while (it.hasNext()) {
1439+
out.startTag(null, "b");
1440+
out.attribute(null, "packageName", it.next());
1441+
out.endTag(null, "b");
1442+
}
1443+
13781444
out.endTag(null, "gs");
13791445

13801446
out.endDocument();
@@ -1445,6 +1511,11 @@ void readStateFromFileLocked(FileInputStream stream) {
14451511
.parseInt(parser.getAttributeValue(null, "id"), 16);
14461512
mHosts.add(host);
14471513
}
1514+
} else if ("b".equals(tag)) {
1515+
String packageName = parser.getAttributeValue(null, "packageName");
1516+
if (packageName != null) {
1517+
mPackagesWithBindWidgetPermission.add(packageName);
1518+
}
14481519
} else if ("g".equals(tag)) {
14491520
AppWidgetId id = new AppWidgetId();
14501521
id.appWidgetId = Integer.parseInt(parser.getAttributeValue(null, "id"), 16);

0 commit comments

Comments
 (0)