diff options
| author | Svetoslav <svetoslavganov@google.com> | 2014-07-16 15:12:03 -0700 |
|---|---|---|
| committer | Torne (Richard Coles) <torne@google.com> | 2014-08-06 11:01:36 +0100 |
| commit | 39999cf0263d0568337a87d0e586dcafd9e22ba1 (patch) | |
| tree | 1e38f5f5a2d899db203ea4375d11bbb4369fad36 /core/java | |
| parent | 8a0ee34c7047b0b3c0869b0c173a210b40fd3050 (diff) | |
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.
(cherrypicked 8cd27c3c to work around Gerrit issue)
Change-Id: I267260b55d74c48b112a29979a9f59eef7a8194e
Diffstat (limited to 'core/java')
18 files changed, 814 insertions, 386 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5f606a611c7d..4cf8cb4b3523 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 21525bcae6e2..797a0a032a4e 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 82d9d1d65e02..59209239ddf7 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; @@ -40,7 +41,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.security.Credentials; -import android.service.trust.TrustAgentService; import android.util.Log; import com.android.org.conscrypt.TrustedCertificateStore; @@ -57,6 +57,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; @@ -3126,4 +3127,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 000000000000..edd8199384ff --- /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 699323d8fc98..c6fe4f922e68 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -182,4 +182,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 84d3835a086b..e7b68f56ceff 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 700bba816a93..1ff476e877ad 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 e5bf7d0caa79..e5d1d95d9787 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 8b9c7f0f5d7d..e4dad5ab36db 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><receiver></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><receiver></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><receiver></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><receiver></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><receiver></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 f3a7b1c1bcbf..74172083ce82 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 4e1c4a774448..ad7c350aa24e 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 e5a52926c6ed..0eda69238aa7 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 5b80648b15d5..1716dbd86f2b 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 5c7a43b541a2..1098fa23db37 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 bbe6f9ea83f7..5d21e0bff589 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 b152297275a8..cf1f5542f493 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 6d51d389f3b3..a7f7fe19bb4d 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 5214dd904af8..9da1c9d90f25 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); } |
