New API to allow third-party apps to bind widgets

Change-Id: I1a3761c1a0f557a32d4d3bdd0207567fec918ba7
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.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.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 @@
     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 @@
         }
     }
 
-    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 @@
         }
     }
 
+    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 @@
                 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 @@
                                     .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);