diff options
author | Svetoslav <svetoslavganov@google.com> | 2014-07-16 15:12:03 -0700 |
---|---|---|
committer | Svetoslav Ganov <svetoslavganov@google.com> | 2014-08-05 20:57:20 +0000 |
commit | 976e8bd2017d0263216c62111454438cc0f130e3 (patch) | |
tree | 5cf592fb85841f9e41d3bf6b43422641c3609ab2 /core | |
parent | c79eabcd3c6306bb2ec75f9584b79e661f265127 (diff) | |
download | frameworks_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')
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><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 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); } |