summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorSvetoslav <svetoslavganov@google.com>2014-07-16 15:12:03 -0700
committerSvetoslav Ganov <svetoslavganov@google.com>2014-08-05 20:57:20 +0000
commit976e8bd2017d0263216c62111454438cc0f130e3 (patch)
tree5cf592fb85841f9e41d3bf6b43422641c3609ab2 /core
parentc79eabcd3c6306bb2ec75f9584b79e661f265127 (diff)
downloadframeworks_base-976e8bd2017d0263216c62111454438cc0f130e3.zip
frameworks_base-976e8bd2017d0263216c62111454438cc0f130e3.tar.gz
frameworks_base-976e8bd2017d0263216c62111454438cc0f130e3.tar.bz2
Allow adding widgets from user profiles.
The goal of this change is to enable support for appwidget from user profiles to the user main profile. A user profile is a user which is associated as a child of the main user profile. For example, a user may have a personal (parent) and corporate (child) profile. The device policy should be able to control whether adding a widget from a child profile and given packages is allowed. This change assumes that all packages from managed profiles are white listed. Another change will add the device policy changes. Change-Id: I267260b55d74c48b112a29979a9f59eef7a8194e
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/ContextImpl.java28
-rw-r--r--core/java/android/app/Notification.java22
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java86
-rw-r--r--core/java/android/app/admin/DevicePolicyManagerInternal.java38
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/appwidget/AppWidgetHost.java131
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java27
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java413
-rw-r--r--core/java/android/appwidget/AppWidgetProviderInfo.java140
-rw-r--r--core/java/android/content/Context.java11
-rw-r--r--core/java/android/content/ContextWrapper.java6
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java2
-rw-r--r--core/java/android/widget/AnalogClock.java10
-rw-r--r--core/java/android/widget/RemoteViews.java167
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java42
-rw-r--r--core/java/android/widget/ViewFlipper.java14
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetHost.aidl9
-rw-r--r--core/java/com/android/internal/appwidget/IAppWidgetService.aidl53
18 files changed, 814 insertions, 388 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f606a6..4cf8cb4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -18,10 +18,12 @@ package android.app;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
+import android.appwidget.AppWidgetManager;
import android.os.Build;
import android.service.persistentdata.IPersistentDataBlockService;
import android.service.persistentdata.PersistentDataBlockManager;
+import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.policy.PolicyManager;
import com.android.internal.util.Preconditions;
@@ -148,7 +150,6 @@ import android.app.trust.TrustManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
-import com.android.internal.appwidget.IAppWidgetService.Stub;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.telecomm.ITelecommService;
@@ -771,6 +772,12 @@ class ContextImpl extends Context {
return new MediaProjectionManager(ctx);
}
});
+
+ registerService(APPWIDGET_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(APPWIDGET_SERVICE);
+ return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b));
+ }});
}
static ContextImpl getImpl(Context context) {
@@ -2100,6 +2107,25 @@ class ContextImpl extends Context {
}
@Override
+ public Context createApplicationContext(ApplicationInfo application, int flags)
+ throws NameNotFoundException {
+ LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+ flags | CONTEXT_REGISTER_PACKAGE);
+ if (pi != null) {
+ final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
+ new UserHandle(UserHandle.getUserId(application.uid)), restricted,
+ mDisplay, mOverrideConfiguration);
+ if (c.mResources != null) {
+ return c;
+ }
+ }
+
+ throw new PackageManager.NameNotFoundException(
+ "Application package " + application.packageName + " not found");
+ }
+
+ @Override
public Context createPackageContext(String packageName, int flags)
throws NameNotFoundException {
return createPackageContextAsUser(packageName, flags,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 21525bc..797a0a0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1707,28 +1707,6 @@ public class Notification implements Parcelable
}
}
- /** {@hide} */
- public void setUser(UserHandle user) {
- if (user.getIdentifier() == UserHandle.USER_ALL) {
- user = UserHandle.OWNER;
- }
- if (tickerView != null) {
- tickerView.setUser(user);
- }
- if (contentView != null) {
- contentView.setUser(user);
- }
- if (bigContentView != null) {
- bigContentView.setUser(user);
- }
- if (headsUpContentView != null) {
- headsUpContentView.setUser(user);
- }
- if (publicVersion != null) {
- publicVersion.setUser(user);
- }
- }
-
/**
* @hide
*/
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e28f00c..ca6b1e8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,6 +20,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.app.Activity;
+import android.app.admin.IDevicePolicyManager;
import android.content.AbstractRestrictionsProvider;
import android.content.ComponentName;
import android.content.Context;
@@ -28,8 +29,6 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.RestrictionsManager;
-import android.media.AudioService;
import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.Handler;
@@ -40,7 +39,6 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.service.trust.TrustAgentService;
import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -55,6 +53,7 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -3079,4 +3078,85 @@ public class DevicePolicyManager {
}
return false;
}
+
+ /**
+ * Called by the profile owner to enable widget providers from a given package
+ * to be available in the parent profile. As a result the user will be able to
+ * add widgets from the white-listed package running under the profile to a widget
+ * host which runs under the device owner, for example the home screen. Note that
+ * a package may have zero or more provider components, where each component
+ * provides a different widget type.
+ * <p>
+ * <strong>Note:</strong> By default no widget provider package is white-listed.
+ * </p>
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The package from which widget providers are white-listed.
+ * @return Whether the package was added.
+ *
+ * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
+ * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
+ */
+ public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ if (mService != null) {
+ try {
+ return mService.addCrossProfileWidgetProvider(admin, packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error calling addCrossProfileWidgetProvider", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by the profile owner to disable widget providers from a given package
+ * to be available in the parent profile. For this method to take effect the
+ * package should have been added via {@link #addCrossProfileWidgetProvider(
+ * android.content.ComponentName, String)}.
+ * <p>
+ * <strong>Note:</strong> By default no widget provider package is white-listed.
+ * </p>
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The package from which widget providers are no longer
+ * white-listed.
+ * @return Whether the package was removed.
+ *
+ * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
+ * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
+ */
+ public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ if (mService != null) {
+ try {
+ return mService.removeCrossProfileWidgetProvider(admin, packageName);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error calling removeCrossProfileWidgetProvider", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by the profile owner to query providers from which packages are
+ * available in the parent profile.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return The white-listed package list.
+ *
+ * @see #addCrossProfileWidgetProvider(android.content.ComponentName, String)
+ * @see #removeCrossProfileWidgetProvider(android.content.ComponentName, String)
+ */
+ public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+ if (mService != null) {
+ try {
+ List<String> providers = mService.getCrossProfileWidgetProviders(admin);
+ if (providers != null) {
+ return providers;
+ }
+ } catch (RemoteException re) {
+ Log.w(TAG, "Error calling getCrossProfileWidgetProviders", re);
+ }
+ }
+ return Collections.emptyList();
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
new file mode 100644
index 0000000..edd8199
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 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 android.app.admin;
+
+import java.util.List;
+
+/**
+ * Device policy manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class DevicePolicyManagerInternal {
+
+ /**
+ * Gets the packages whose widget providers are white-listed to be
+ * available in the parent user.
+ *
+ * @param profileId The profile id.
+ * @return The list of packages if such or empty list if there are
+ * no white-listed packages or the profile id is not a managed
+ * profile.
+ */
+ public abstract List<String> getCrossProfileWidgetProviders(int profileId);
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6ce737a..8954c0d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -175,4 +175,7 @@ interface IDevicePolicyManager {
void setTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, in List<String> features, int userId);
List<String> getTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, int userId);
+ boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
+ boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
+ List<String> getCrossProfileWidgetProviders(in ComponentName admin);
}
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 84d3835..e7b68f5 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -19,7 +19,11 @@ package android.appwidget;
import java.util.ArrayList;
import java.util.HashMap;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -61,32 +65,30 @@ public class AppWidgetHost {
private OnClickHandler mOnClickHandler;
class Callbacks extends IAppWidgetHost.Stub {
- public void updateAppWidget(int appWidgetId, RemoteViews views, int userId) {
+ public void updateAppWidget(int appWidgetId, RemoteViews views) {
if (isLocalBinder() && views != null) {
views = views.clone();
- views.setUser(new UserHandle(userId));
}
- Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, userId, views);
+ Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
msg.sendToTarget();
}
- public void providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId) {
+ public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
if (isLocalBinder() && info != null) {
info = info.clone();
}
Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED,
- appWidgetId, userId, info);
+ appWidgetId, 0, info);
msg.sendToTarget();
}
- public void providersChanged(int userId) {
- Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0);
- msg.sendToTarget();
+ public void providersChanged() {
+ mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED).sendToTarget();
}
- public void viewDataChanged(int appWidgetId, int viewId, int userId) {
+ public void viewDataChanged(int appWidgetId, int viewId) {
Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED,
- appWidgetId, viewId, userId);
+ appWidgetId, viewId);
msg.sendToTarget();
}
}
@@ -99,7 +101,7 @@ public class AppWidgetHost {
public void handleMessage(Message msg) {
switch (msg.what) {
case HANDLE_UPDATE: {
- updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj, msg.arg2);
+ updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
break;
}
case HANDLE_PROVIDER_CHANGED: {
@@ -111,7 +113,7 @@ public class AppWidgetHost {
break;
}
case HANDLE_VIEW_DATA_CHANGED: {
- viewDataChanged(msg.arg1, msg.arg2, (Integer) msg.obj);
+ viewDataChanged(msg.arg1, msg.arg2);
break;
}
}
@@ -151,25 +153,20 @@ public class AppWidgetHost {
public void startListening() {
int[] updatedIds;
ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
-
- final int userId = mContext.getUserId();
try {
if (mPackageName == null) {
mPackageName = mContext.getPackageName();
}
- updatedIds = sService.startListening(
- mCallbacks, mPackageName, mHostId, updatedViews, userId);
+ updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId,
+ updatedViews);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
final int N = updatedIds.length;
- for (int i=0; i<N; i++) {
- if (updatedViews.get(i) != null) {
- updatedViews.get(i).setUser(new UserHandle(userId));
- }
- updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId);
+ for (int i = 0; i < N; i++) {
+ updateAppWidgetView(updatedIds[i], updatedViews.get(i));
}
}
@@ -179,7 +176,7 @@ public class AppWidgetHost {
*/
public void stopListening() {
try {
- sService.stopListening(mHostId, mContext.getUserId());
+ sService.stopListening(mPackageName, mHostId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -200,7 +197,7 @@ public class AppWidgetHost {
if (mPackageName == null) {
mPackageName = mContext.getPackageName();
}
- return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
+ return sService.allocateAppWidgetId(mPackageName, mHostId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -208,18 +205,39 @@ public class AppWidgetHost {
}
/**
- * Get a appWidgetId for a host in the given package.
+ * Starts an app widget provider configure activity for result on behalf of the caller.
+ * Use this method if the provider is in another profile as you are not allowed to start
+ * an activity in another profile. The provided intent must have action {@link
+ * AppWidgetManager#ACTION_APPWIDGET_CONFIGURE}. The widget id must be specified by
+ * the {@link AppWidgetManager#EXTRA_APPWIDGET_ID} extra. The provider configure
+ * activity must be specified as the component name of the intent via {@link
+ * Intent#setComponent(android.content.ComponentName)}. The user profile under which
+ * the provider operates is specified via the {@link
+ * AppWidgetManager#EXTRA_APPWIDGET_PROVIDER_PROFILE} extra. If a user profile is
+ * not provided, the current user is used. Finally, you can optionally provide a
+ * request code that is returned in {@link Activity#onActivityResult(int, int,
+ * android.content.Intent)}.
*
- * @return a appWidgetId
- * @hide
+ * @param activity The activity from which to start the configure one.
+ * @param intent Properly populated intent for launching the configuration activity.
+ * @param requestCode Optional request code retuned with the result.
+ *
+ * @throws android.content.ActivityNotFoundException If the activity is not found.
+ *
+ * @see AppWidgetProviderInfo#getProfile()
*/
- public static int allocateAppWidgetIdForPackage(int hostId, int userId, String packageName) {
- checkCallerIsSystem();
+ public final void startAppWidgetConfigureActivityForResult(Activity activity, Intent intent,
+ int requestCode) {
try {
- if (sService == null) {
- bindService();
+ IntentSender intentSender = sService.createAppWidgetConfigIntentSender(
+ mContext.getPackageName(), intent);
+ if (intentSender != null) {
+ activity.startIntentSenderForResult(intentSender, requestCode, null, 0, 0, 0);
+ } else {
+ throw new ActivityNotFoundException();
}
- return sService.allocateAppWidgetId(packageName, hostId, userId);
+ } catch (IntentSender.SendIntentException e) {
+ throw new ActivityNotFoundException();
} catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
@@ -235,20 +253,12 @@ public class AppWidgetHost {
if (sService == null) {
bindService();
}
- return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId());
+ return sService.getAppWidgetIdsForHost(mContext.getPackageName(), mHostId);
} catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
}
- private static void checkCallerIsSystem() {
- int uid = Process.myUid();
- if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
- return;
- }
- throw new SecurityException("Disallowed call for uid " + uid);
- }
-
private boolean isLocalBinder() {
return Process.myPid() == Binder.getCallingPid();
}
@@ -260,7 +270,7 @@ public class AppWidgetHost {
synchronized (mViews) {
mViews.remove(appWidgetId);
try {
- sService.deleteAppWidgetId(appWidgetId, mContext.getUserId());
+ sService.deleteAppWidgetId(mContext.getPackageName(), appWidgetId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -269,22 +279,6 @@ public class AppWidgetHost {
}
/**
- * Stop listening to changes for this AppWidget.
- * @hide
- */
- public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) {
- checkCallerIsSystem();
- try {
- if (sService == null) {
- bindService();
- }
- sService.deleteAppWidgetId(appWidgetId, userId);
- } catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
- }
-
- /**
* Remove all records about this host from the AppWidget manager.
* <ul>
* <li>Call this when initializing your database, as it might be because of a data wipe.</li>
@@ -294,7 +288,7 @@ public class AppWidgetHost {
*/
public void deleteHost() {
try {
- sService.deleteHost(mHostId, mContext.getUserId());
+ sService.deleteHost(mContext.getPackageName(), mHostId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -310,16 +304,8 @@ public class AppWidgetHost {
* </ul>
*/
public static void deleteAllHosts() {
- deleteAllHosts(UserHandle.myUserId());
- }
-
- /**
- * Private method containing a userId
- * @hide
- */
- public static void deleteAllHosts(int userId) {
try {
- sService.deleteAllHosts(userId);
+ sService.deleteAllHosts();
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -332,9 +318,7 @@ public class AppWidgetHost {
*/
public final AppWidgetHostView createView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- final int userId = mContext.getUserId();
AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget);
- view.setUserId(userId);
view.setOnClickHandler(mOnClickHandler);
view.setAppWidget(appWidgetId, appWidget);
synchronized (mViews) {
@@ -342,10 +326,7 @@ public class AppWidgetHost {
}
RemoteViews views;
try {
- views = sService.getAppWidgetViews(appWidgetId, userId);
- if (views != null) {
- views.setUser(new UserHandle(mContext.getUserId()));
- }
+ views = sService.getAppWidgetViews(mPackageName, appWidgetId);
} catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
@@ -397,7 +378,7 @@ public class AppWidgetHost {
// Does nothing
}
- void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) {
+ void updateAppWidgetView(int appWidgetId, RemoteViews views) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
@@ -407,7 +388,7 @@ public class AppWidgetHost {
}
}
- void viewDataChanged(int appWidgetId, int viewId, int userId) {
+ void viewDataChanged(int appWidgetId, int viewId) {
AppWidgetHostView v;
synchronized (mViews) {
v = mViews.get(appWidgetId);
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 700bba8..1ff476e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -87,7 +87,6 @@ public class AppWidgetHostView extends FrameLayout {
Bitmap mOld;
Paint mOldPaint = new Paint();
private OnClickHandler mOnClickHandler;
- private UserHandle mUser;
/**
* Create a host view. Uses default fade animations.
@@ -115,17 +114,11 @@ public class AppWidgetHostView extends FrameLayout {
public AppWidgetHostView(Context context, int animationIn, int animationOut) {
super(context);
mContext = context;
- mUser = Process.myUserHandle();
// We want to segregate the view ids within AppWidgets to prevent
// problems when those ids collide with view ids in the AppWidgetHost.
setIsRootNamespace(true);
}
- /** @hide */
- public void setUserId(int userId) {
- mUser = new UserHandle(userId);
- }
-
/**
* Pass the given handler to RemoteViews when updating this widget. Unless this
* is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)}
@@ -380,7 +373,7 @@ public class AppWidgetHostView extends FrameLayout {
} else {
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
- mRemoteContext = getRemoteContext(remoteViews);
+ mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
// If our stale view has been prepared to match active, and the new
@@ -466,17 +459,14 @@ public class AppWidgetHostView extends FrameLayout {
* Build a {@link Context} cloned into another package name, usually for the
* purposes of reading remote resources.
*/
- private Context getRemoteContext(RemoteViews views) {
- // Bail if missing package name
- final String packageName = views.getPackage();
- if (packageName == null) return mContext;
-
+ private Context getRemoteContext() {
try {
// Return if cloned successfully, otherwise default
- return mContext.createPackageContextAsUser(packageName, Context.CONTEXT_RESTRICTED,
- mUser);
+ return mContext.createApplicationContext(
+ mInfo.providerInfo.applicationInfo,
+ Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
- Log.e(TAG, "Package name " + packageName + " not found");
+ Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found");
return mContext;
}
}
@@ -548,8 +538,7 @@ public class AppWidgetHostView extends FrameLayout {
try {
if (mInfo != null) {
- Context theirContext = mContext.createPackageContextAsUser(
- mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED, mUser);
+ Context theirContext = getRemoteContext();
mRemoteContext = theirContext;
LayoutInflater inflater = (LayoutInflater)
theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -572,8 +561,6 @@ public class AppWidgetHostView extends FrameLayout {
} else {
Log.w(TAG, "can't inflate defaultView because mInfo is missing");
}
- } catch (PackageManager.NameNotFoundException e) {
- exception = e;
} catch (RuntimeException e) {
exception = e;
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index e5bf7d0..e5d1d95 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -21,8 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -30,9 +30,8 @@ import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetService;
-import java.lang.ref.WeakReference;
+import java.util.Collections;
import java.util.List;
-import java.util.WeakHashMap;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -45,7 +44,6 @@ import java.util.WeakHashMap;
* </div>
*/
public class AppWidgetManager {
- static final String TAG = "AppWidgetManager";
/**
* Activity action to launch from your {@link AppWidgetHost} activity when you want to
@@ -72,9 +70,9 @@ public class AppWidgetManager {
* <p>
* When you receive the result from the AppWidget pick activity, if the resultCode is
* {@link android.app.Activity#RESULT_OK}, an AppWidget has been selected. 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.
+ * 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
*/
@@ -103,6 +101,12 @@ public class AppWidgetManager {
* <td>The BroadcastReceiver that will be the AppWidget provider for this AppWidget.
* </td>
* </tr>
+ * <tr>
+ * <td>{@link #EXTRA_APPWIDGET_PROVIDER_PROFILE}</td>
+ * <td>An optional handle to a user profile under which runs the provider
+ * for this AppWidget.
+ * </td>
+ * </tr>
* </table>
*
* <p>
@@ -119,8 +123,7 @@ public class AppWidgetManager {
* {@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.
+ * should delete the appWidgetId.
*
* @see #ACTION_APPWIDGET_CONFIGURE
*
@@ -130,7 +133,8 @@ public class AppWidgetManager {
/**
* 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}.
+ * to the activity specified in the {@link AppWidgetProviderInfo AppWidgetProviderInfo
+ * meta-data}.
*
* <p>
* The intent will contain the following extras:
@@ -145,7 +149,8 @@ public class AppWidgetManager {
* {@link android.app.Activity#setResult Activity.setResult()}, the AppWidget will be added,
* and you will receive an {@link #ACTION_APPWIDGET_UPDATE} broadcast for this AppWidget.
* If you return {@link android.app.Activity#RESULT_CANCELED}, the host will cancel the add
- * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED} broadcast.
+ * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED}
+ * broadcast.
*/
public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
@@ -188,7 +193,9 @@ public class AppWidgetManager {
/**
* An intent extra which points to a bundle of extra information for a particular widget id.
- * In particular this bundle can contain EXTRA_APPWIDGET_WIDTH and EXTRA_APPWIDGET_HEIGHT.
+ * In particular this bundle can contain {@link #OPTION_APPWIDGET_MIN_WIDTH},
+ * {@link #OPTION_APPWIDGET_MIN_HEIGHT}, {@link #OPTION_APPWIDGET_MAX_WIDTH},
+ * {@link #OPTION_APPWIDGET_MAX_HEIGHT}.
*/
public static final String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
@@ -203,11 +210,19 @@ public class AppWidgetManager {
/**
* An intent extra that contains the component name of a AppWidget provider.
* <p>
- * The value will be an ComponentName.
+ * The value will be an {@link android.content.ComponentName}.
*/
public static final String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
/**
+ * An intent extra that contains the user handle of the profile under
+ * which an AppWidget provider is registered.
+ * <p>
+ * The value will be a {@link android.os.UserHandle}.
+ */
+ public static final String EXTRA_APPWIDGET_PROVIDER_PROFILE = "appWidgetProviderProfile";
+
+ /**
* 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).
@@ -295,12 +310,12 @@ public class AppWidgetManager {
public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
/**
- * Sent when an instance of an AppWidget is removed from the last host.
+ * Sent when the last AppWidget of this provider is removed from the last host.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
*
- * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
+ * @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context)
*/
public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -403,46 +418,36 @@ public class AppWidgetManager {
*/
public static final String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
- static WeakHashMap<Context, WeakReference<AppWidgetManager>> sManagerCache =
- new WeakHashMap<Context, WeakReference<AppWidgetManager>>();
- static IAppWidgetService sService;
+ private final String mPackageName;
- Context mContext;
+ private final IAppWidgetService mService;
- private DisplayMetrics mDisplayMetrics;
+ private final DisplayMetrics mDisplayMetrics;
/**
* Get the AppWidgetManager instance to use for the supplied {@link android.content.Context
* Context} object.
*/
public static AppWidgetManager getInstance(Context context) {
- synchronized (sManagerCache) {
- if (sService == null) {
- IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
- sService = IAppWidgetService.Stub.asInterface(b);
- }
-
- WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
- AppWidgetManager result = null;
- if (ref != null) {
- result = ref.get();
- }
- if (result == null) {
- result = new AppWidgetManager(context);
- sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
- }
- return result;
- }
+ return (AppWidgetManager) context.getSystemService(Context.APPWIDGET_SERVICE);
}
- private AppWidgetManager(Context context) {
- mContext = context;
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context in which to operate.
+ * @param service The backing system service.
+ * @hide
+ */
+ public AppWidgetManager(Context context, IAppWidgetService service) {
+ mPackageName = context.getPackageName();
+ mService = service;
mDisplayMetrics = context.getResources().getDisplayMetrics();
}
/**
* Set the RemoteViews to use for the specified appWidgetIds.
- *
+ * <p>
* Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
* contain a complete representation of the widget. For performing partial widget updates, see
* {@link #partiallyUpdateAppWidget(int[], RemoteViews)}.
@@ -456,12 +461,15 @@ public class AppWidgetManager {
* The total Bitmap memory used by the RemoteViews object cannot exceed that required to
* fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes.
*
- * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
- * @param views The RemoteViews object to show.
+ * @param appWidgetIds The AppWidget instances for which to set the RemoteViews.
+ * @param views The RemoteViews object to show.
*/
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.updateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
+ mService.updateAppWidgetIds(mPackageName, appWidgetIds, views);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -470,18 +478,21 @@ public class AppWidgetManager {
/**
* Update the extras for a given widget instance.
- *
+ * <p>
* The extras can be used to embed additional information about this widget to be accessed
* by the associated widget's AppWidgetProvider.
*
* @see #getAppWidgetOptions(int)
*
- * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
- * @param options The options to associate with this widget
+ * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
+ * @param options The options to associate with this widget
*/
public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.updateAppWidgetOptions(appWidgetId, options, mContext.getUserId());
+ mService.updateAppWidgetOptions(mPackageName, appWidgetId, options);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -490,18 +501,21 @@ public class AppWidgetManager {
/**
* Get the extras associated with a given widget instance.
- *
+ * <p>
* The extras can be used to embed additional information about this widget to be accessed
* by the associated widget's AppWidgetProvider.
*
* @see #updateAppWidgetOptions(int, Bundle)
*
- * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
- * @return The options associated with the given widget instance.
+ * @param appWidgetId The AppWidget instances for which to set the RemoteViews.
+ * @return The options associated with the given widget instance.
*/
public Bundle getAppWidgetOptions(int appWidgetId) {
+ if (mService == null) {
+ return Bundle.EMPTY;
+ }
try {
- return sService.getAppWidgetOptions(appWidgetId, mContext.getUserId());
+ return mService.getAppWidgetOptions(mPackageName, appWidgetId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -510,7 +524,7 @@ public class AppWidgetManager {
/**
* Set the RemoteViews to use for the specified appWidgetId.
- *
+ * <p>
* Note that the RemoteViews parameter will be cached by the AppWidgetService, and hence should
* contain a complete representation of the widget. For performing partial widget updates, see
* {@link #partiallyUpdateAppWidget(int, RemoteViews)}.
@@ -528,12 +542,15 @@ public class AppWidgetManager {
* @param views The RemoteViews object to show.
*/
public void updateAppWidget(int appWidgetId, RemoteViews views) {
+ if (mService == null) {
+ return;
+ }
updateAppWidget(new int[] { appWidgetId }, views);
}
/**
* Perform an incremental update or command on the widget(s) specified by appWidgetIds.
- *
+ * <p>
* This update differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
* RemoteViews object which is passed is understood to be an incomplete representation of the
* widget, and hence does not replace the cached representation of the widget. As of API
@@ -556,8 +573,11 @@ public class AppWidgetManager {
* @param views The RemoteViews object containing the incremental update / command.
*/
public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.partiallyUpdateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
+ mService.partiallyUpdateAppWidgetIds(mPackageName, appWidgetIds, views);
} catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
@@ -565,7 +585,7 @@ public class AppWidgetManager {
/**
* Perform an incremental update or command on the widget specified by appWidgetId.
- *
+ * <p>
* This update differs from {@link #updateAppWidget(int, RemoteViews)} in that the RemoteViews
* object which is passed is understood to be an incomplete representation of the widget, and
* hence is not cached by the AppWidgetService. Note that because these updates are not cached,
@@ -588,6 +608,9 @@ public class AppWidgetManager {
* @param views The RemoteViews object containing the incremental update / command.
*/
public void partiallyUpdateAppWidget(int appWidgetId, RemoteViews views) {
+ if (mService == null) {
+ return;
+ }
partiallyUpdateAppWidget(new int[] { appWidgetId }, views);
}
@@ -605,8 +628,11 @@ public class AppWidgetManager {
* @param views The RemoteViews object to show.
*/
public void updateAppWidget(ComponentName provider, RemoteViews views) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.updateAppWidgetProvider(provider, views, mContext.getUserId());
+ mService.updateAppWidgetProvider(provider, views);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -621,8 +647,11 @@ public class AppWidgetManager {
* @param viewId The collection view id.
*/
public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId, mContext.getUserId());
+ mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -637,37 +666,106 @@ public class AppWidgetManager {
* @param viewId The collection view id.
*/
public void notifyAppWidgetViewDataChanged(int appWidgetId, int viewId) {
+ if (mService == null) {
+ return;
+ }
notifyAppWidgetViewDataChanged(new int[] { appWidgetId }, viewId);
}
/**
+ * Gets the AppWidget providers for the given user profiles. User profiles can only
+ * be the current user or a profile of the current user. For example, the current
+ * user may have a corporate profile. In this case the parent user profile has a
+ * child profile, the corporate one.
+ *
+ * @param profiles The profiles for which to get providers. Passing null is equivaled
+ * to passing only the current user handle.
+ * @return The intalled providers.
+ *
+ * @see android.os.Process#myUserHandle()
+ * @see android.os.UserManager#getUserProfiles()
+ */
+ public List<AppWidgetProviderInfo> getInstalledProvidersForProfiles(UserHandle[] profiles) {
+ if (mService == null) {
+ return Collections.emptyList();
+ }
+ return getInstalledProvidersForProfiles(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ profiles);
+ }
+
+ /**
* Return a list of the AppWidget providers that are currently installed.
*/
public List<AppWidgetProviderInfo> getInstalledProviders() {
- return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+ if (mService == null) {
+ return Collections.emptyList();
+ }
+ return getInstalledProvidersForProfiles(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ null);
}
/**
- * Return a list of the AppWidget providers that are currently installed.
+ * Gets the AppWidget providers for the current user.
*
* @param categoryFilter Will only return providers which register as any of the specified
* specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
+ * @return The intalled providers.
+ *
+ * @see android.os.Process#myUserHandle()
+ * @see android.os.UserManager#getUserProfiles()
+ *
* @hide
*/
public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
+ if (mService == null) {
+ return Collections.emptyList();
+ }
+ return getInstalledProvidersForProfiles(categoryFilter, null);
+ }
+
+ /**
+ * Gets the AppWidget providers for the given user profiles. User profiles can only
+ * be the current user or a profile of the current user. For example, the current
+ * user may have a corporate profile. In this case the parent user profile has a
+ * child profile, the corporate one.
+ *
+ * @param categoryFilter Will only return providers which register as any of the specified
+ * specified categories. See {@link AppWidgetProviderInfo#widgetCategory}.
+ * @param profiles Child profiles of the current user which to be queried. The user
+ * is itself also a profile. If null, the providers only for the current user
+ * are returned.
+ * @return The intalled providers.
+ *
+ * @see android.os.Process#myUserHandle()
+ * @see android.os.UserManager#getUserProfiles()
+ *
+ * @hide
+ */
+ public List<AppWidgetProviderInfo> getInstalledProvidersForProfiles(int categoryFilter,
+ UserHandle[] profiles) {
+ if (mService == null) {
+ return Collections.emptyList();
+ }
+
+ int[] profileIds = null;
+
+ if (profiles != null) {
+ final int profileCount = profiles.length;
+ profileIds = new int[profileCount];
+ for (int i = 0; i < profileCount; i++) {
+ profileIds[i] = profiles[i].getIdentifier();
+ }
+ }
+
try {
- List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter,
- mContext.getUserId());
+ List<AppWidgetProviderInfo> providers = mService.getInstalledProviders(categoryFilter,
+ profileIds);
+ if (providers == null) {
+ return Collections.emptyList();
+ }
for (AppWidgetProviderInfo info : providers) {
// Converting complex to dp.
- info.minWidth =
- TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
- info.minHeight =
- TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
- info.minResizeWidth =
- TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
- info.minResizeHeight =
- TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
+ convertSizesToPixels(info);
}
return providers;
}
@@ -683,19 +781,14 @@ public class AppWidgetManager {
* you don't have access to that appWidgetId, null is returned.
*/
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+ if (mService == null) {
+ return null;
+ }
try {
- AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId,
- mContext.getUserId());
+ AppWidgetProviderInfo info = mService.getAppWidgetInfo(mPackageName, appWidgetId);
if (info != null) {
// Converting complex to dp.
- info.minWidth =
- TypedValue.complexToDimensionPixelSize(info.minWidth, mDisplayMetrics);
- info.minHeight =
- TypedValue.complexToDimensionPixelSize(info.minHeight, mDisplayMetrics);
- info.minResizeWidth =
- TypedValue.complexToDimensionPixelSize(info.minResizeWidth, mDisplayMetrics);
- info.minResizeHeight =
- TypedValue.complexToDimensionPixelSize(info.minResizeHeight, mDisplayMetrics);
+ convertSizesToPixels(info);
}
return info;
}
@@ -717,12 +810,10 @@ public class AppWidgetManager {
* @hide
*/
public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
- try {
- sService.bindAppWidgetId(appWidgetId, provider, null, mContext.getUserId());
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
+ if (mService == null) {
+ return;
}
+ bindAppWidgetId(appWidgetId, provider, null);
}
/**
@@ -741,12 +832,10 @@ public class AppWidgetManager {
* @hide
*/
public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
- try {
- sService.bindAppWidgetId(appWidgetId, provider, options, mContext.getUserId());
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
+ if (mService == null) {
+ return;
}
+ bindAppWidgetIdIfAllowed(appWidgetId, Process.myUserHandle(), provider, options);
}
/**
@@ -757,22 +846,16 @@ public class AppWidgetManager {
* 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 appWidgetId The AppWidget id under which to bind the provider.
* @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) {
+ if (mService == null) {
return false;
}
- try {
- return sService.bindAppWidgetIdIfAllowed(
- mContext.getPackageName(), appWidgetId, provider, null, mContext.getUserId());
- }
- catch (RemoteException e) {
- throw new RuntimeException("system server dead?", e);
- }
+ return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, null);
}
/**
@@ -783,7 +866,7 @@ public class AppWidgetManager {
* 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 appWidgetId The AppWidget id under which to bind the provider.
* @param provider The {@link android.content.BroadcastReceiver} that will be the AppWidget
* provider for this AppWidget.
* @param options Bundle containing options for the AppWidget. See also
@@ -793,12 +876,52 @@ public class AppWidgetManager {
*/
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, ComponentName provider,
Bundle options) {
- if (mContext == null) {
+ if (mService == null) {
+ return false;
+ }
+ return bindAppWidgetIdIfAllowed(appWidgetId, UserHandle.myUserId(), provider, options);
+ }
+
+ /**
+ * Set the provider for a given appWidgetId if the caller has a permission.
+ * <p>
+ * <strong>Note:</strong> You need the {@link android.Manifest.permission#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.
+ * </p>
+ *
+ * @param appWidgetId The AppWidget id under which to bind the provider.
+ * @param user The user id in which the provider resides.
+ * @param provider The component name of the provider.
+ * @param options An optional Bundle containing options for the AppWidget.
+ *
+ * @return true if this component has permission to bind the AppWidget
+ */
+ public boolean bindAppWidgetIdIfAllowed(int appWidgetId, UserHandle user,
+ ComponentName provider, Bundle options) {
+ if (mService == null) {
+ return false;
+ }
+ return bindAppWidgetIdIfAllowed(appWidgetId, user.getIdentifier(), provider, options);
+ }
+
+ /**
+ * 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
+ * @param userId The user id of the user under which the package runs.
+ * @return true if the package was granted permission by the user to bind app widgets
+ * @hide
+ */
+ public boolean hasBindAppWidgetPermission(String packageName, int userId) {
+ if (mService == null) {
return false;
}
try {
- return sService.bindAppWidgetIdIfAllowed(mContext.getPackageName(), appWidgetId,
- provider, options, mContext.getUserId());
+ return mService.hasBindAppWidgetPermission(packageName, userId);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -815,8 +938,11 @@ public class AppWidgetManager {
* @hide
*/
public boolean hasBindAppWidgetPermission(String packageName) {
+ if (mService == null) {
+ return false;
+ }
try {
- return sService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
+ return mService.hasBindAppWidgetPermission(packageName, UserHandle.myUserId());
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -828,13 +954,35 @@ public class AppWidgetManager {
*
* <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
+ * @param packageName 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) {
+ if (mService == null) {
+ return;
+ }
+ setBindAppWidgetPermission(packageName, UserHandle.myUserId(), permission);
+ }
+
+ /**
+ * 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 packageName The package whose permission is being changed
+ * @param userId The user under which the package is running.
+ * @param permission Whether to give the package permission to bind widgets
+ *
+ * @hide
+ */
+ public void setBindAppWidgetPermission(String packageName, int userId, boolean permission) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.setBindAppWidgetPermission(packageName, permission, mContext.getUserId());
+ mService.setBindAppWidgetPermission(packageName, userId, permission);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -847,18 +995,20 @@ public class AppWidgetManager {
* The appWidgetId specified must already be bound to the calling AppWidgetHost via
* {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
*
+ * @param packageName The package from which the binding is requested.
* @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.
- * @param userHandle The user to bind to.
* @hide
*/
- public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
- UserHandle userHandle) {
+ public void bindRemoteViewsService(String packageName, int appWidgetId, Intent intent,
+ IBinder connection) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.bindRemoteViewsService(appWidgetId, intent, connection,
- userHandle.getIdentifier());
+ mService.bindRemoteViewsService(packageName, appWidgetId, intent, connection);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -871,15 +1021,18 @@ public class AppWidgetManager {
* The appWidgetId specified muse already be bound to the calling AppWidgetHost via
* {@link android.appwidget.AppWidgetManager#bindAppWidgetId AppWidgetManager.bindAppWidgetId()}.
*
+ * @param packageName The package from which the binding is requested.
* @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 userHandle The user to unbind from.
* @hide
*/
- public void unbindRemoteViewsService(int appWidgetId, Intent intent, UserHandle userHandle) {
+ public void unbindRemoteViewsService(String packageName, int appWidgetId, Intent intent) {
+ if (mService == null) {
+ return;
+ }
try {
- sService.unbindRemoteViewsService(appWidgetId, intent, userHandle.getIdentifier());
+ mService.unbindRemoteViewsService(packageName, appWidgetId, intent);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
@@ -894,12 +1047,40 @@ public class AppWidgetManager {
* AppWidget provider to find appWidgetIds for.
*/
public int[] getAppWidgetIds(ComponentName provider) {
+ if (mService == null) {
+ return new int[0];
+ }
try {
- return sService.getAppWidgetIds(provider, mContext.getUserId());
+ return mService.getAppWidgetIds(provider);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
}
-}
+ private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId,
+ ComponentName provider, Bundle options) {
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.bindAppWidgetId(mPackageName, appWidgetId,
+ profileId, provider, options);
+ }
+ catch (RemoteException e) {
+ throw new RuntimeException("system server dead?", e);
+ }
+ }
+
+ private void convertSizesToPixels(AppWidgetProviderInfo info) {
+ // Converting complex to dp.
+ info.minWidth = TypedValue.complexToDimensionPixelSize(info.minWidth,
+ mDisplayMetrics);
+ info.minHeight = TypedValue.complexToDimensionPixelSize(info.minHeight,
+ mDisplayMetrics);
+ info.minResizeWidth = TypedValue.complexToDimensionPixelSize(info.minResizeWidth,
+ mDisplayMetrics);
+ info.minResizeHeight = TypedValue.complexToDimensionPixelSize(info.minResizeHeight,
+ mDisplayMetrics);
+ }
+}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 8b9c7f0..e4dad5a 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -16,9 +16,17 @@
package android.appwidget;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.content.ComponentName;
+import android.os.UserHandle;
+import android.os.UserManager;
/**
* Describes the meta data for an installed AppWidget provider. The fields in this class
@@ -145,21 +153,23 @@ public class AppWidgetProviderInfo implements Parcelable {
public ComponentName configure;
/**
- * The label to display to the user in the AppWidget picker. If not supplied in the
- * xml, the application label will be used.
+ * The label to display to the user in the AppWidget picker.
*
- * <p>This field corresponds to the <code>android:label</code> attribute in
- * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ * @deprecated Use {@link #loadLabel(android.content.pm.PackageManager)}.
*/
+ @Deprecated
public String label;
/**
- * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the
+ * The icon to display for this AppWidget in the AppWidget picker. If not supplied in the
* xml, the application icon will be used.
*
* <p>This field corresponds to the <code>android:icon</code> attribute in
* the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ *
+ * @deprecated Use {@link #loadIcon(android.content.Context, int)}.
*/
+ @Deprecated
public int icon;
/**
@@ -176,7 +186,10 @@ public class AppWidgetProviderInfo implements Parcelable {
*
* <p>This field corresponds to the <code>android:previewImage</code> attribute in
* the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ *
+ * @deprecated User {@link #loadPreviewImage(android.content.Context, int)}.
*/
+ @Deprecated
public int previewImage;
/**
@@ -200,12 +213,17 @@ public class AppWidgetProviderInfo implements Parcelable {
*/
public int widgetCategory;
+ /** @hide */
+ public ActivityInfo providerInfo;
+
public AppWidgetProviderInfo() {
+
}
/**
* Unflatten the AppWidgetProviderInfo from a parcel.
*/
+ @SuppressWarnings("deprecation")
public AppWidgetProviderInfo(Parcel in) {
if (0 != in.readInt()) {
this.provider = new ComponentName(in);
@@ -226,8 +244,86 @@ public class AppWidgetProviderInfo implements Parcelable {
this.autoAdvanceViewId = in.readInt();
this.resizeMode = in.readInt();
this.widgetCategory = in.readInt();
+ this.providerInfo = in.readParcelable(null);
+ }
+
+ /**
+ * Loads the localized label to display to the user in the AppWidget picker.
+ *
+ * @param packageManager Package manager instance for loading resources.
+ * @return The label for the current locale.
+ */
+ public final String loadLabel(PackageManager packageManager) {
+ CharSequence label = providerInfo.loadLabel(packageManager);
+ if (label != null) {
+ return label.toString().trim();
+ }
+ return null;
+ }
+
+ /**
+ * Loads the icon to display for this AppWidget in the AppWidget picker. If not
+ * supplied in the xml, the application icon will be used. A client can optionally
+ * provide a desired density such as {@link android.util.DisplayMetrics#DENSITY_LOW}
+ * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is
+ * provided, the density of the current display will be used.
+ * <p>
+ * The loaded icon corresponds to the <code>android:icon</code> attribute in
+ * the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> If you care about widgets from different profiles, you
+ * should use this method to load the icon as the system will apply the correct
+ * badging when applicable, so the user knows which profile a widget comes from.
+ * </p>
+ *
+ * @param context Context for accessing resources.
+ * @param density The optional desired density as per
+ * {@link android.util.DisplayMetrics#densityDpi}.
+ * @return The potentially badged provider icon.
+ */
+ public final Drawable loadIcon(Context context, int density) {
+ return loadDrawable(context, density, providerInfo.getIconResource());
}
+ /**
+ * Loads a preview of what the AppWidget will look like after it's configured.
+ * If not supplied, the AppWidget's icon will be used. A client can optionally
+ * provide a desired deinsity such as {@link android.util.DisplayMetrics#DENSITY_LOW}
+ * {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is
+ * provided, the density of the current display will be used.
+ * <p>
+ * The loaded image corresponds to the <code>android:previewImage</code> attribute
+ * in the <code>&lt;receiver&gt;</code> element in the AndroidManifest.xml file.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> If you care about widgets from different profiles, you
+ * should use this method to load the preview image as the system will apply the
+ * correct badging when applicable, so the user knows which profile a previewed
+ * widget comes from.
+ * </p>
+ *
+ * @param context Context for accessing resources.
+ * @param density The optional desired density as per
+ * {@link android.util.DisplayMetrics#densityDpi}.
+ * @return The potentially badged widget preview image.
+ */
+ @SuppressWarnings("deprecation")
+ public final Drawable loadPreviewImage(Context context, int density) {
+ return loadDrawable(context, density, previewImage);
+ }
+
+ /**
+ * Gets the user profile in which the provider resides.
+ *
+ * @return The hosting user profile.
+ */
+ public final UserHandle getProfile() {
+ return new UserHandle(UserHandle.getUserId(providerInfo.applicationInfo.uid));
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
public void writeToParcel(android.os.Parcel out, int flags) {
if (this.provider != null) {
out.writeInt(1);
@@ -254,9 +350,11 @@ public class AppWidgetProviderInfo implements Parcelable {
out.writeInt(this.autoAdvanceViewId);
out.writeInt(this.resizeMode);
out.writeInt(this.widgetCategory);
+ out.writeParcelable(this.providerInfo, flags);
}
@Override
+ @SuppressWarnings("deprecation")
public AppWidgetProviderInfo clone() {
AppWidgetProviderInfo that = new AppWidgetProviderInfo();
that.provider = this.provider == null ? null : this.provider.clone();
@@ -273,7 +371,8 @@ public class AppWidgetProviderInfo implements Parcelable {
that.previewImage = this.previewImage;
that.autoAdvanceViewId = this.autoAdvanceViewId;
that.resizeMode = this.resizeMode;
- that.widgetCategory = this.widgetCategory;
+ that.widgetCategory = this.widgetCategory;
+ that.providerInfo = this.providerInfo;
return that;
}
@@ -281,6 +380,33 @@ public class AppWidgetProviderInfo implements Parcelable {
return 0;
}
+ private Drawable loadDrawable(Context context, int density, int resourceId) {
+ try {
+ Resources resources = context.getPackageManager().getResourcesForApplication(
+ providerInfo.applicationInfo);
+
+ final Drawable drawable;
+ if (resourceId > 0) {
+ if (density <= 0) {
+ density = context.getResources().getDisplayMetrics().densityDpi;
+ }
+ drawable = resources.getDrawableForDensity(resourceId, density);
+ } else {
+ drawable = providerInfo.loadIcon(context.getPackageManager());
+ }
+
+ if (drawable instanceof BitmapDrawable) {
+ UserManager userManager = (UserManager) context.getSystemService(
+ Context.USER_SERVICE);
+ return userManager.getBadgedDrawableForUser(drawable, getProfile());
+ }
+ } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+ /* ignore */
+ }
+
+ return null;
+ }
+
/**
* Parcelable.Creator that instantiates AppWidgetProviderInfo objects
*/
@@ -299,6 +425,6 @@ public class AppWidgetProviderInfo implements Parcelable {
};
public String toString() {
- return "AppWidgetProviderInfo(provider=" + this.provider + ")";
+ return "AppWidgetProviderInfo(" + getProfile() + '/' + provider + ')';
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f3a7b1c1..7417208 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2076,7 +2076,7 @@ public abstract class Context {
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
TEXT_SERVICES_MANAGER_SERVICE,
- //@hide: APPWIDGET_SERVICE,
+ APPWIDGET_SERVICE,
//@hide: BACKUP_SERVICE,
DROPBOX_SERVICE,
DEVICE_POLICY_SERVICE,
@@ -2596,7 +2596,6 @@ public abstract class Context {
* Use with {@link #getSystemService} to retrieve a
* {@link android.appwidget.AppWidgetManager} for accessing AppWidgets.
*
- * @hide
* @see #getSystemService
*/
public static final String APPWIDGET_SERVICE = "appwidget";
@@ -3306,6 +3305,14 @@ public abstract class Context {
throws PackageManager.NameNotFoundException;
/**
+ * Creates a context given an {@link android.content.pm.ApplicationInfo}.
+ *
+ * @hide
+ */
+ public abstract Context createApplicationContext(ApplicationInfo application,
+ int flags) throws PackageManager.NameNotFoundException;
+
+ /**
* Get the userId associated with this context
* @return user id
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4e1c4a7..ad7c350 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -666,6 +666,12 @@ public class ContextWrapper extends Context {
}
/** @hide */
+ public Context createApplicationContext(ApplicationInfo application,
+ int flags) throws PackageManager.NameNotFoundException {
+ return mBase.createApplicationContext(application, flags);
+ }
+
+ /** @hide */
@Override
public int getUserId() {
return mBase.getUserId();
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index e5a5292..0eda692 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -63,7 +63,6 @@ public class StatusBarNotification implements Parcelable {
this.score = score;
this.notification = notification;
this.user = user;
- this.notification.setUser(user);
this.postTime = postTime;
this.key = key();
this.groupKey = groupKey();
@@ -83,7 +82,6 @@ public class StatusBarNotification implements Parcelable {
this.score = in.readInt();
this.notification = new Notification(in);
this.user = UserHandle.readFromParcel(in);
- this.notification.setUser(this.user);
this.postTime = in.readLong();
this.key = key();
this.groupKey = groupKey();
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 5b80648..1716dbd 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -111,7 +111,15 @@ public class AnalogClock extends View {
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- getContext().registerReceiver(mIntentReceiver, filter, null, mHandler);
+ // OK, this is gross but needed. This class is supported by the
+ // remote views machanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For exmaple, when adding widgets from a user profile to the
+ // home screen. Therefore, we register the receiver as the current
+ // user not the one the context is for.
+ getContext().registerReceiverAsUser(mIntentReceiver,
+ android.os.Process.myUserHandle(), filter, null, mHandler);
}
// NOTE: It's safe to do these after registering the receiver since the receiver always runs
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5c7a43b..1098fa2 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -17,6 +17,7 @@
package android.widget;
import android.app.ActivityOptions;
+import android.app.ActivityThread;
import android.app.PendingIntent;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
@@ -73,11 +74,11 @@ public class RemoteViews implements Parcelable, Filter {
static final String EXTRA_REMOTEADAPTER_APPWIDGET_ID = "remoteAdapterAppWidgetId";
/**
- * User that these views should be applied as. Requires
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} when
- * crossing user boundaries.
+ * Application that hosts the remote views.
+ *
+ * @hide
*/
- private UserHandle mUser = android.os.Process.myUserHandle();
+ private ApplicationInfo mApplication;
/**
* The package name of the package containing the layout
@@ -275,9 +276,9 @@ public class RemoteViews implements Parcelable, Filter {
/**
* Merges the passed RemoteViews actions with this RemoteViews actions according to
* action-specific merge rules.
- *
+ *
* @param newRv
- *
+ *
* @hide
*/
public void mergeRemoteViews(RemoteViews newRv) {
@@ -1608,16 +1609,16 @@ public class RemoteViews implements Parcelable, Filter {
int bpp = 4;
if (c != null) {
switch (c) {
- case ALPHA_8:
- bpp = 1;
- break;
- case RGB_565:
- case ARGB_4444:
- bpp = 2;
- break;
- case ARGB_8888:
- bpp = 4;
- break;
+ case ALPHA_8:
+ bpp = 1;
+ break;
+ case RGB_565:
+ case ARGB_4444:
+ bpp = 2;
+ break;
+ case ARGB_8888:
+ bpp = 4;
+ break;
}
}
increment(b.getWidth() * b.getHeight() * bpp);
@@ -1637,17 +1638,13 @@ public class RemoteViews implements Parcelable, Filter {
mPackage = packageName;
mLayoutId = layoutId;
mBitmapCache = new BitmapCache();
+ mApplication = ActivityThread.currentApplication().getApplicationInfo();
// setup the memory usage statistics
mMemoryUsageCounter = new MemoryUsageCounter();
recalculateMemoryUsage();
}
- /** {@hide} */
- public void setUser(UserHandle user) {
- mUser = user;
- }
-
private boolean hasLandscapeAndPortraitLayouts() {
return (mLandscape != null) && (mPortrait != null);
}
@@ -1713,53 +1710,53 @@ public class RemoteViews implements Parcelable, Filter {
for (int i=0; i<count; i++) {
int tag = parcel.readInt();
switch (tag) {
- case SetOnClickPendingIntent.TAG:
- mActions.add(new SetOnClickPendingIntent(parcel));
- break;
- case SetDrawableParameters.TAG:
- mActions.add(new SetDrawableParameters(parcel));
- break;
- case ReflectionAction.TAG:
- mActions.add(new ReflectionAction(parcel));
- break;
- case ViewGroupAction.TAG:
- mActions.add(new ViewGroupAction(parcel, mBitmapCache));
- break;
- case ReflectionActionWithoutParams.TAG:
- mActions.add(new ReflectionActionWithoutParams(parcel));
- break;
- case SetEmptyView.TAG:
- mActions.add(new SetEmptyView(parcel));
- break;
- case SetPendingIntentTemplate.TAG:
- mActions.add(new SetPendingIntentTemplate(parcel));
- break;
- case SetOnClickFillInIntent.TAG:
- mActions.add(new SetOnClickFillInIntent(parcel));
- break;
- case SetRemoteViewsAdapterIntent.TAG:
- mActions.add(new SetRemoteViewsAdapterIntent(parcel));
- break;
- case TextViewDrawableAction.TAG:
- mActions.add(new TextViewDrawableAction(parcel));
- break;
- case TextViewSizeAction.TAG:
- mActions.add(new TextViewSizeAction(parcel));
- break;
- case ViewPaddingAction.TAG:
- mActions.add(new ViewPaddingAction(parcel));
- break;
- case BitmapReflectionAction.TAG:
- mActions.add(new BitmapReflectionAction(parcel));
- break;
- case SetRemoteViewsAdapterList.TAG:
- mActions.add(new SetRemoteViewsAdapterList(parcel));
- break;
- case TextViewDrawableColorFilterAction.TAG:
- mActions.add(new TextViewDrawableColorFilterAction(parcel));
- break;
- default:
- throw new ActionException("Tag " + tag + " not found");
+ case SetOnClickPendingIntent.TAG:
+ mActions.add(new SetOnClickPendingIntent(parcel));
+ break;
+ case SetDrawableParameters.TAG:
+ mActions.add(new SetDrawableParameters(parcel));
+ break;
+ case ReflectionAction.TAG:
+ mActions.add(new ReflectionAction(parcel));
+ break;
+ case ViewGroupAction.TAG:
+ mActions.add(new ViewGroupAction(parcel, mBitmapCache));
+ break;
+ case ReflectionActionWithoutParams.TAG:
+ mActions.add(new ReflectionActionWithoutParams(parcel));
+ break;
+ case SetEmptyView.TAG:
+ mActions.add(new SetEmptyView(parcel));
+ break;
+ case SetPendingIntentTemplate.TAG:
+ mActions.add(new SetPendingIntentTemplate(parcel));
+ break;
+ case SetOnClickFillInIntent.TAG:
+ mActions.add(new SetOnClickFillInIntent(parcel));
+ break;
+ case SetRemoteViewsAdapterIntent.TAG:
+ mActions.add(new SetRemoteViewsAdapterIntent(parcel));
+ break;
+ case TextViewDrawableAction.TAG:
+ mActions.add(new TextViewDrawableAction(parcel));
+ break;
+ case TextViewSizeAction.TAG:
+ mActions.add(new TextViewSizeAction(parcel));
+ break;
+ case ViewPaddingAction.TAG:
+ mActions.add(new ViewPaddingAction(parcel));
+ break;
+ case BitmapReflectionAction.TAG:
+ mActions.add(new BitmapReflectionAction(parcel));
+ break;
+ case SetRemoteViewsAdapterList.TAG:
+ mActions.add(new SetRemoteViewsAdapterList(parcel));
+ break;
+ case TextViewDrawableColorFilterAction.TAG:
+ mActions.add(new TextViewDrawableColorFilterAction(parcel));
+ break;
+ default:
+ throw new ActionException("Tag " + tag + " not found");
}
}
}
@@ -1771,6 +1768,8 @@ public class RemoteViews implements Parcelable, Filter {
mLayoutId = mPortrait.getLayoutId();
}
+ mApplication = parcel.readParcelable(null);
+
// setup the memory usage statistics
mMemoryUsageCounter = new MemoryUsageCounter();
recalculateMemoryUsage();
@@ -2557,22 +2556,32 @@ public class RemoteViews implements Parcelable, Filter {
}
private Context prepareContext(Context context) {
- Context c;
- String packageName = mPackage;
+ if (mApplication != null) {
+ if (context.getUserId() == UserHandle.getUserId(mApplication.uid)
+ && context.getPackageName().equals(mApplication.packageName)) {
+ return context;
+ }
+ try {
+ return context.createApplicationContext(mApplication,
+ Context.CONTEXT_RESTRICTED);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Package name " + mPackage + " not found");
+ }
+ }
- if (packageName != null) {
+ if (mPackage != null) {
+ if (context.getPackageName().equals(mPackage)) {
+ return context;
+ }
try {
- c = context.createPackageContextAsUser(
- packageName, Context.CONTEXT_RESTRICTED, mUser);
+ return context.createPackageContext(
+ mPackage, Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
- Log.e(LOG_TAG, "Package name " + packageName + " not found");
- c = context;
+ Log.e(LOG_TAG, "Package name " + mPackage + " not found");
}
- } else {
- c = context;
}
- return c;
+ return context;
}
/**
@@ -2629,6 +2638,8 @@ public class RemoteViews implements Parcelable, Filter {
mLandscape.writeToParcel(dest, flags);
mPortrait.writeToParcel(dest, flags);
}
+
+ dest.writeParcelable(mApplication, 0);
}
/**
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index bbe6f9e..5d21e0b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -114,8 +114,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// construction (happens when we have a cached FixedSizeRemoteViewsCache).
private boolean mDataReady = false;
- int mUserId;
-
/**
* An interface for the RemoteAdapter to notify other classes when adapters
* are actually connected to/disconnected from their actual services.
@@ -159,9 +157,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
if ((adapter = mAdapter.get()) != null) {
- checkInteractAcrossUsersPermission(context, adapter.mUserId);
- mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(),
- new UserHandle(adapter.mUserId));
+ mgr.bindRemoteViewsService(context.getPackageName(), appWidgetId,
+ intent, asBinder());
} else {
Slog.w(TAG, "bind: adapter was null");
}
@@ -179,9 +176,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
RemoteViewsAdapter adapter;
final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
if ((adapter = mAdapter.get()) != null) {
- checkInteractAcrossUsersPermission(context, adapter.mUserId);
- mgr.unbindRemoteViewsService(appWidgetId, intent,
- new UserHandle(adapter.mUserId));
+ mgr.unbindRemoteViewsService(context.getPackageName(), appWidgetId, intent);
} else {
Slog.w(TAG, "unbind: adapter was null");
}
@@ -796,12 +791,10 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
static class RemoteViewsCacheKey {
final Intent.FilterComparison filter;
final int widgetId;
- final int userId;
- RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId, int userId) {
+ RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId) {
this.filter = filter;
this.widgetId = widgetId;
- this.userId = userId;
}
@Override
@@ -810,29 +803,28 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
return false;
}
RemoteViewsCacheKey other = (RemoteViewsCacheKey) o;
- return other.filter.equals(filter) && other.widgetId == widgetId
- && other.userId == userId;
+ return other.filter.equals(filter) && other.widgetId == widgetId;
}
@Override
public int hashCode() {
- return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2) ^ (userId << 10);
+ return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2);
}
}
- public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) {
+ public RemoteViewsAdapter(Context context, Intent intent,
+ RemoteAdapterConnectionCallback callback) {
mContext = context;
mIntent = intent;
+
mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+
mLayoutInflater = LayoutInflater.from(context);
if (mIntent == null) {
throw new IllegalArgumentException("Non-null Intent must be specified.");
}
mRequestedViews = new RemoteViewsFrameLayoutRefSet();
- checkInteractAcrossUsersPermission(context, UserHandle.myUserId());
- mUserId = context.getUserId();
-
// 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);
@@ -855,7 +847,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
mServiceConnection = new RemoteViewsAdapterServiceConnection(this);
RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent),
- mAppWidgetId, mUserId);
+ mAppWidgetId);
synchronized(sCachedRemoteViewsCaches) {
if (sCachedRemoteViewsCaches.containsKey(key)) {
@@ -876,15 +868,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
}
}
- private static void checkInteractAcrossUsersPermission(Context context, int userId) {
- if (context.getUserId() != userId
- && context.checkCallingOrSelfPermission(MULTI_USER_PERM)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must have permission " + MULTI_USER_PERM
- + " to inflate another user's widget");
- }
- }
-
@Override
protected void finalize() throws Throwable {
try {
@@ -906,7 +889,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
public void saveRemoteViewsCache() {
final RemoteViewsCacheKey key = new RemoteViewsCacheKey(
- new Intent.FilterComparison(mIntent), mAppWidgetId, mUserId);
+ new Intent.FilterComparison(mIntent), mAppWidgetId);
synchronized(sCachedRemoteViewsCaches) {
// If we already have a remove runnable posted for this key, remove it.
@@ -1028,7 +1011,6 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
long itemId = 0;
try {
remoteViews = factory.getViewAt(position);
- remoteViews.setUser(new UserHandle(mUserId));
itemId = factory.getItemId(position);
} catch (RemoteException e) {
Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage());
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index b152297..cf1f554 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -21,8 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.TypedArray;
-import android.os.Handler;
-import android.os.Message;
+import android.os.*;
import android.util.AttributeSet;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
@@ -90,7 +89,16 @@ public class ViewFlipper extends ViewAnimator {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
- getContext().registerReceiver(mReceiver, filter, null, mHandler);
+
+ // OK, this is gross but needed. This class is supported by the
+ // remote views machanism and as a part of that the remote views
+ // can be inflated by a context for another user without the app
+ // having interact users permission - just for loading resources.
+ // For exmaple, when adding widgets from a user profile to the
+ // home screen. Therefore, we register the receiver as the current
+ // user not the one the context is for.
+ getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
+ filter, null, mHandler);
if (mAutoStart) {
// Automatically start when requested
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index 6d51d38..a7f7fe1 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -16,15 +16,16 @@
package com.android.internal.appwidget;
+import android.content.pm.ApplicationInfo;
import android.content.ComponentName;
import android.appwidget.AppWidgetProviderInfo;
import android.widget.RemoteViews;
/** {@hide} */
oneway interface IAppWidgetHost {
- void updateAppWidget(int appWidgetId, in RemoteViews views, int userId);
- void providerChanged(int appWidgetId, in AppWidgetProviderInfo info, int userId);
- void providersChanged(int userId);
- void viewDataChanged(int appWidgetId, int viewId, int userId);
+ void updateAppWidget(int appWidgetId, in RemoteViews views);
+ void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
+ void providersChanged();
+ void viewDataChanged(int appWidgetId, int viewId);
}
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 5214dd9..9da1c9d 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -18,6 +18,8 @@ package com.android.internal.appwidget;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
import android.appwidget.AppWidgetProviderInfo;
import com.android.internal.appwidget.IAppWidgetHost;
import android.os.Bundle;
@@ -30,34 +32,37 @@ interface IAppWidgetService {
//
// for AppWidgetHost
//
- int[] startListening(IAppWidgetHost host, String packageName, int hostId,
- out List<RemoteViews> updatedViews, int userId);
- void stopListening(int hostId, int userId);
- int allocateAppWidgetId(String packageName, int hostId, int userId);
- void deleteAppWidgetId(int appWidgetId, int userId);
- void deleteHost(int hostId, int userId);
- void deleteAllHosts(int userId);
- RemoteViews getAppWidgetViews(int appWidgetId, int userId);
- int[] getAppWidgetIdsForHost(int hostId, int userId);
+ int[] startListening(IAppWidgetHost host, String callingPackage, int hostId,
+ out List<RemoteViews> updatedViews);
+ void stopListening(String callingPackage, int hostId);
+ int allocateAppWidgetId(String callingPackage, int hostId);
+ void deleteAppWidgetId(String callingPackage, int appWidgetId);
+ void deleteHost(String packageName, int hostId);
+ void deleteAllHosts();
+ RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId);
+ int[] getAppWidgetIdsForHost(String callingPackage, int hostId);
+ IntentSender createAppWidgetConfigIntentSender(String callingPackage, in Intent intent);
//
// for AppWidgetManager
//
- void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
- void updateAppWidgetOptions(int appWidgetId, in Bundle extras, int userId);
- Bundle getAppWidgetOptions(int appWidgetId, int userId);
- void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
- void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views, int userId);
- void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId, int userId);
- List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId);
- AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId);
+ void updateAppWidgetIds(String callingPackage, in int[] appWidgetIds, in RemoteViews views);
+ void updateAppWidgetOptions(String callingPackage, int appWidgetId, in Bundle extras);
+ Bundle getAppWidgetOptions(String callingPackage, int appWidgetId);
+ void partiallyUpdateAppWidgetIds(String callingPackage, in int[] appWidgetIds,
+ in RemoteViews views);
+ void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
+ void notifyAppWidgetViewDataChanged(String packageName, in int[] appWidgetIds, int viewId);
+ List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter,
+ in int[] profileIds);
+ AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId);
boolean hasBindAppWidgetPermission(in String packageName, int userId);
- void setBindAppWidgetPermission(in String packageName, in boolean permission, int userId);
- void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options, int userId);
- boolean bindAppWidgetIdIfAllowed(in String packageName, int appWidgetId,
- in ComponentName provider, in Bundle options, int userId);
- void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
- void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
- int[] getAppWidgetIds(in ComponentName provider, int userId);
+ void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission);
+ boolean bindAppWidgetId(in String callingPackage, int appWidgetId,
+ int providerProfileId, in ComponentName providerComponent, in Bundle options);
+ void bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent,
+ in IBinder connection);
+ void unbindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent);
+ int[] getAppWidgetIds(in ComponentName providerComponent);
}