diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-08-02 19:14:39 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-08-02 19:14:39 -0700 |
commit | 329465c6b2407ee086a171cac1a3581f83dbb048 (patch) | |
tree | 0d69eee745692b133e3f2c955561c0e09807ffb5 | |
parent | 478923885da8fac0c1e0a8b7d85b98d1988504bc (diff) | |
parent | b4163a6e12ee7100c758c6d3d062ade1f2843fce (diff) | |
download | frameworks_base-329465c6b2407ee086a171cac1a3581f83dbb048.zip frameworks_base-329465c6b2407ee086a171cac1a3581f83dbb048.tar.gz frameworks_base-329465c6b2407ee086a171cac1a3581f83dbb048.tar.bz2 |
Merge "Add APIs for interacting across users." into jb-mr1-dev
21 files changed, 275 insertions, 49 deletions
diff --git a/api/current.txt b/api/current.txt index ee5aac2..4b43318 100644 --- a/api/current.txt +++ b/api/current.txt @@ -62,6 +62,7 @@ package android { field public static final java.lang.String INJECT_EVENTS = "android.permission.INJECT_EVENTS"; field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER"; field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES"; + field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS"; field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW"; field public static final java.lang.String INTERNET = "android.permission.INTERNET"; field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES"; @@ -893,6 +894,7 @@ package android { field public static final int shownWeekCount = 16843585; // 0x1010341 field public static final int shrinkColumns = 16843082; // 0x101014a field public static final deprecated int singleLine = 16843101; // 0x101015d + field public static final int singleUser = 16843711; // 0x10103bf field public static final int smallIcon = 16843422; // 0x101029e field public static final int smallScreens = 16843396; // 0x1010284 field public static final int smoothScrollbar = 16843313; // 0x1010231 @@ -5317,6 +5319,7 @@ package android.content { method public abstract void revokeUriPermission(android.net.Uri, int); method public abstract void sendBroadcast(android.content.Intent); method public abstract void sendBroadcast(android.content.Intent, java.lang.String); + method public void sendBroadcastToUser(android.content.Intent, int); method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String); method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle); method public abstract void sendStickyBroadcast(android.content.Intent); @@ -6700,6 +6703,7 @@ package android.content.pm { method public void dump(android.util.Printer, java.lang.String); field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2 + field public static final int FLAG_SINGLE_USER = 4; // 0x4 field public static final int FLAG_STOP_WITH_TASK = 1; // 0x1 field public int flags; field public java.lang.String permission; diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 22e454f..ba05ee7 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -896,7 +896,7 @@ public class AccountManagerService private void sendAccountsChangedBroadcast(int userId) { Log.i(TAG, "the accounts changed, sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT, userId); + mContext.sendBroadcastToUser(ACCOUNTS_CHANGED_INTENT, userId); } public void clearPassword(Account account) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 74fce62..3c8a290 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -966,9 +966,8 @@ class ContextImpl extends Context { } } - /** @hide */ @Override - public void sendBroadcast(Intent intent, int userId) { + public void sendBroadcastToUser(Intent intent, int userId) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); try { intent.setAllowFds(false); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 8597993..af8b213 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -988,12 +988,14 @@ public abstract class Context { public abstract void sendBroadcast(Intent intent); /** - * Same as #sendBroadcast(Intent intent), but for a specific user. Used by the system only. + * Same as #sendBroadcast(Intent intent), but for a specific user. This broadcast + * can only be sent to receivers that are part of the calling application. It + * requires holding the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} + * permission. * @param intent the intent to broadcast * @param userId user to send the intent to - * @hide */ - public void sendBroadcast(Intent intent, int userId) { + public void sendBroadcastToUser(Intent intent, int userId) { throw new RuntimeException("Not implemented. Must override in a subclass."); } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 6b950e0..7738132 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -312,10 +312,9 @@ public class ContextWrapper extends Context { mBase.sendBroadcast(intent); } - /** @hide */ @Override - public void sendBroadcast(Intent intent, int userId) { - mBase.sendBroadcast(intent, userId); + public void sendBroadcastToUser(Intent intent, int userId) { + mBase.sendBroadcastToUser(intent, userId); } @Override diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index f8898c1..3ce7c78 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2703,7 +2703,7 @@ public class PackageParser { return null; } - final boolean setExported = sa.hasValue( + boolean setExported = sa.hasValue( com.android.internal.R.styleable.AndroidManifestService_exported); if (setExported) { s.info.exported = sa.getBoolean( @@ -2729,6 +2729,18 @@ public class PackageParser { false)) { s.info.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestService_singleUser, + false)) { + s.info.flags |= ServiceInfo.FLAG_SINGLE_USER; + if (s.info.exported) { + Slog.w(TAG, "Service exported request ignored due to singleUser: " + + s.className + " at " + mArchiveSourcePath + " " + + parser.getPositionDescription()); + s.info.exported = false; + } + setExported = true; + } sa.recycle(); diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 7ee84ab..1aaceb4 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -49,6 +49,13 @@ public class ServiceInfo extends ComponentInfo public static final int FLAG_ISOLATED_PROCESS = 0x0002; /** + * Bit in {@link #flags}: If set, a single instance of the service will + * run for all users on the device. Set from the + * {@link android.R.attr#singleUser} attribute. + */ + public static final int FLAG_SINGLE_USER = 0x0004; + + /** * Options that have been set in the service declaration in the * manifest. * These include: diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9253f24..d636713 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -760,6 +760,25 @@ android:label="@string/permlab_getTasks" android:description="@string/permdesc_getTasks" /> + <!-- Allows an application to call APIs that allow it to do interactions + across the users on the device, using singleton services and + user-targeted broadcasts. This permission is not available to + third party applications. --> + <permission android:name="android.permission.INTERACT_ACROSS_USERS" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="signature|system|development" + android:label="@string/permlab_interactAcrossUsers" + android:description="@string/permdesc_interactAcrossUsers" /> + + <!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS} + that removes restrictions on where broadcasts can be sent and allows other + types of interactions. --> + <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" + android:permissionGroup="android.permission-group.SYSTEM_TOOLS" + android:protectionLevel="signature" + android:label="@string/permlab_interactAcrossUsersFull" + android:description="@string/permdesc_interactAcrossUsersFull" /> + <!-- Allows an application to get full detailed information about recently running tasks, with full fidelity to the real state. @hide --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index f971d39..8bc1e79 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1275,6 +1275,16 @@ that is isolated from the rest of the system. The only communication with it is through the Service API (binding and starting). --> <attr name="isolatedProcess" format="boolean" /> + <!-- If set to true, a single instance of this service will run for + all users. That instance will run as user 0, the default/primary + user. When the app running in processes for other users interacts + with this service (by binding to it, starting it, etc) they will + always interact with the instance running for user 0. Enabling + single user mode forces "exported" of the service to be false, to + avoid introducing multi-user security bugs. You must hold the + permission {@link android.Manifest.permission#INTERACT_ACROSS_USERS} in order + to use this feature. --> + <attr name="singleUser" format="boolean" /> </declare-styleable> <!-- The <code>receiver</code> tag declares an diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b9b8a1b..4881a94 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3660,7 +3660,7 @@ <public type="style" name="Widget.DeviceDefault.Light.MediaRouteButton" id="0x010301d8" /> <!-- =============================================================== - Resources added in version 17 of the platform (Jelly Bean MRx?) + Resources added in version 17 of the platform (Jelly Bean MR1) =============================================================== --> <eat-comment /> <public type="attr" name="supportsRtl" /> @@ -3679,5 +3679,6 @@ <public type="attr" name="layout_alignParentEnd" /> <public type="attr" name="listPreferredItemPaddingStart" /> <public type="attr" name="listPreferredItemPaddingEnd" /> - + <public type="attr" name="singleUser" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b757fe8..b369744 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -543,6 +543,19 @@ about currently and recently running tasks. This may allow the app to discover information about which applications are used on the device.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permlab_interactAcrossUsers">interact across users</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permdesc_interactAcrossUsers">Allows the app to perform actions + across different users on the device. Malicious apps may use this to violate + the protection between users.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permlab_interactAcrossUsersFull">full license to interact across users</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> + <string name="permdesc_interactAcrossUsersFull">Allows all possible interactions across + users.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50] --> <string name="permlab_getDetailedTasks">retrieve details of running apps</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index 46b968a..48f967c 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -499,7 +499,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED); intent.setComponent(p.info.provider); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastToUser(intent, mUserId); if (p.instances.size() == 0) { // cancel the future updates cancelBroadcasts(p); @@ -507,7 +507,7 @@ class AppWidgetServiceImpl { // send the broacast saying that the provider is not in use any more intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastToUser(intent, mUserId); } } } @@ -880,7 +880,7 @@ class AppWidgetServiceImpl { intent.setComponent(p.info.provider); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastToUser(intent, mUserId); } } @@ -1205,7 +1205,7 @@ class AppWidgetServiceImpl { void sendEnableIntentLocked(Provider p) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastToUser(intent, mUserId); } void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { @@ -1213,7 +1213,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent, mUserId); + mContext.sendBroadcastToUser(intent, mUserId); } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 26ebb98..3b4200a 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -11140,9 +11140,8 @@ public final class ActivityManagerService extends ActivityManagerNative private ServiceLookupResult retrieveServiceLocked(Intent service, String resolvedType, int callingPid, int callingUid, int userId) { ServiceRecord r = null; - if (DEBUG_SERVICE) - Slog.v(TAG, "retrieveServiceLocked: " + service + " type=" + resolvedType - + " callingUid=" + callingUid); + if (DEBUG_SERVICE) Slog.v(TAG, "retrieveServiceLocked: " + service + + " type=" + resolvedType + " callingUid=" + callingUid); if (service.getComponent() != null) { r = mServiceMap.getServiceByName(service.getComponent(), userId); @@ -11163,14 +11162,29 @@ public final class ActivityManagerService extends ActivityManagerNative ": not found"); return null; } + ComponentName name = new ComponentName( + sInfo.applicationInfo.packageName, sInfo.name); if (userId > 0) { - if (isSingleton(sInfo.processName, sInfo.applicationInfo)) { + if (isSingleton(sInfo.processName, sInfo.applicationInfo) + || (sInfo.flags&ServiceInfo.FLAG_SINGLE_USER) != 0) { userId = 0; + } else if ((sInfo.flags&ServiceInfo.FLAG_SINGLE_USER) != 0) { + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + callingPid, callingUid, -1, true) + == PackageManager.PERMISSION_GRANTED) { + userId = 0; + } else { + String msg = "Permission Denial: Service " + name + + " requests FLAG_SINGLE_USER, but app does not hold " + + android.Manifest.permission.INTERACT_ACROSS_USERS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } } + sInfo = new ServiceInfo(sInfo); sInfo.applicationInfo = getAppInfoForUser(sInfo.applicationInfo, userId); } - ComponentName name = new ComponentName( - sInfo.applicationInfo.packageName, sInfo.name); r = mServiceMap.getServiceByName(name, userId); if (r == null) { Intent.FilterComparison filter = new Intent.FilterComparison( @@ -11531,11 +11545,11 @@ public final class ActivityManagerService extends ActivityManagerNative } final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; - final String appName = r.processName; + final String procName = r.processName; ProcessRecord app; if (!isolated) { - app = getProcessRecordLocked(appName, r.appInfo.uid); + app = getProcessRecordLocked(procName, r.appInfo.uid); if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app); if (app != null && app.thread != null) { @@ -11563,7 +11577,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Not running -- get it started, and enqueue this service record // to be executed when the app comes up. if (app == null) { - if ((app=startProcessLocked(appName, r.appInfo, true, intentFlags, + if ((app=startProcessLocked(procName, r.appInfo, true, intentFlags, "service", r.name, false, isolated)) == null) { Slog.w(TAG, "Unable to launch app " + r.appInfo.packageName + "/" @@ -12668,6 +12682,7 @@ public final class ActivityManagerService extends ActivityManagerNative public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { enforceNotIsolatedCaller("registerReceiver"); + int callingUid; synchronized(this) { ProcessRecord callerApp = null; if (caller != null) { @@ -12683,8 +12698,10 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException("Given caller package " + callerPackage + " is not running in process " + callerApp); } + callingUid = callerApp.info.uid; } else { callerPackage = null; + callingUid = Binder.getCallingUid(); } List allSticky = null; @@ -12729,7 +12746,8 @@ public final class ActivityManagerService extends ActivityManagerNative } mRegisteredReceivers.put(receiver.asBinder(), rl); } - BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, + permission, callingUid); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); @@ -12748,7 +12766,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, null, null, -1, -1, null, receivers, null, 0, null, null, - false, true, true); + false, true, true, false); queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } @@ -12840,7 +12858,34 @@ public final class ActivityManagerService extends ActivityManagerNative if ((resultTo != null) && !ordered) { Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!"); } - + + boolean onlySendToCaller = false; + + // If the caller is trying to send this broadcast to a different + // user, verify that is allowed. + if (UserId.getUserId(callingUid) != userId) { + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + callingPid, callingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + callingPid, callingUid, -1, true) + == PackageManager.PERMISSION_GRANTED) { + onlySendToCaller = true; + } else { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + + " asks to send as user " + userId + + " but is calling from user " + UserId.getUserId(callingUid) + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + } + // Handle special intents: if this broadcast is from the package // manager about a package being removed, we need to remove all of // its activities from the history stack. @@ -13042,7 +13087,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, - ordered, sticky, false); + ordered, sticky, false, onlySendToCaller); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); @@ -13132,7 +13177,7 @@ public final class ActivityManagerService extends ActivityManagerNative BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, - sticky, false); + sticky, false, onlySendToCaller); if (DEBUG_BROADCAST) Slog.v( TAG, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index b49bc22..4e6d0fa 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -27,13 +27,15 @@ class BroadcastFilter extends IntentFilter { final ReceiverList receiverList; final String packageName; final String requiredPermission; + final int owningUid; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, - String _packageName, String _requiredPermission) { + String _packageName, String _requiredPermission, int _owningUid) { super(_filter); receiverList = _receiverList; packageName = _packageName; requiredPermission = _requiredPermission; + owningUid = _owningUid; } public void dump(PrintWriter pw, String prefix) { diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 47b8c0a..c6d46fc 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -369,7 +369,17 @@ public class BroadcastQueue { private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered) { boolean skip = false; - if (filter.requiredPermission != null) { + if (r.onlySendToCaller) { + if (!UserId.isSameApp(r.callingUid, filter.owningUid)) { + Slog.w(TAG, "Permission Denial: broadcasting " + + r.intent.toString() + + " from " + r.callerPackage + " (pid=" + + r.callingPid + ", uid=" + r.callingUid + ")" + + " not allowed to go to different app " + filter.owningUid); + skip = true; + } + } + if (!skip && filter.requiredPermission != null) { int perm = mService.checkComponentPermission(filter.requiredPermission, r.callingPid, r.callingUid, -1, true); if (perm != PackageManager.PERMISSION_GRANTED) { @@ -382,7 +392,7 @@ public class BroadcastQueue { skip = true; } } - if (r.requiredPermission != null) { + if (!skip && r.requiredPermission != null) { int perm = mService.checkComponentPermission(r.requiredPermission, filter.receiverList.pid, filter.receiverList.uid, -1, true); if (perm != PackageManager.PERMISSION_GRANTED) { @@ -651,6 +661,17 @@ public class BroadcastQueue { (ResolveInfo)nextReceiver; boolean skip = false; + if (r.onlySendToCaller) { + if (!UserId.isSameApp(r.callingUid, info.activityInfo.applicationInfo.uid)) { + Slog.w(TAG, "Permission Denial: broadcasting " + + r.intent.toString() + + " from " + r.callerPackage + " (pid=" + + r.callingPid + ", uid=" + r.callingUid + ")" + + " not allowed to go to different app " + + info.activityInfo.applicationInfo.uid); + skip = true; + } + } int perm = mService.checkComponentPermission(info.activityInfo.permission, r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid, info.activityInfo.exported); diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index dd560fc..799b609 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -44,6 +44,7 @@ class BroadcastRecord extends Binder { final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? final boolean initialSticky; // initial broadcast from register to sticky? + final boolean onlySendToCaller; // only allow receipt by sender's components? final String requiredPermission; // a permission the caller has required final List receivers; // contains BroadcastFilter and ResolveInfo IIntentReceiver resultTo; // who receives final result if non-null @@ -167,7 +168,7 @@ class BroadcastRecord extends Binder { int _callingPid, int _callingUid, String _requiredPermission, List _receivers, IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized, - boolean _sticky, boolean _initialSticky) { + boolean _sticky, boolean _initialSticky, boolean _onlySendToCaller) { queue = _queue; intent = _intent; callerApp = _callerApp; @@ -183,6 +184,7 @@ class BroadcastRecord extends Binder { ordered = _serialized; sticky = _sticky; initialSticky = _initialSticky; + onlySendToCaller = _onlySendToCaller; nextReceiver = 0; state = IDLE; } diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index bf583e1..92c6676 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -284,9 +284,8 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } - /** @hide */ @Override - public void sendBroadcast(Intent intent, int userId) { + public void sendBroadcastToUser(Intent intent, int userId) { throw new UnsupportedOperationException(); } diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml index de3b6d1..6f00095 100644 --- a/tests/ActivityTests/AndroidManifest.xml +++ b/tests/ActivityTests/AndroidManifest.xml @@ -20,6 +20,7 @@ <uses-permission android:name="android.permission.REORDER_TASKS" /> <uses-permission android:name="android.permission.REMOVE_TASKS" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <application android:label="ActivityTest"> <activity android:name="ActivityTestMain"> <intent-filter> @@ -27,5 +28,10 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <service android:name="SingleUserService" + android:singleUser="true" android:exported="true"> + </service> + <receiver android:name="UserTarget"> + </receiver> </application> </manifest> diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java index ae42e29..bcef2d9 100644 --- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java +++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java @@ -16,37 +16,31 @@ package com.google.android.test.activity; -import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityThread; import android.app.AlertDialog; -import android.app.Application; import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Intent; +import android.content.ServiceConnection; import android.os.Bundle; -import android.graphics.BitmapFactory; +import android.os.IBinder; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ScrollView; -import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.res.CompatibilityInfo; -import android.util.DisplayMetrics; import android.util.Log; public class ActivityTestMain extends Activity { + static final String TAG = "ActivityTest"; + ActivityManager mAm; private void addThumbnail(LinearLayout container, Bitmap bm, @@ -114,6 +108,37 @@ public class ActivityTestMain extends Activity { return true; } }); + menu.add("Bind!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, SingleUserService.class); + ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + Log.i(TAG, "Service connected " + name + " " + service); + } + @Override + public void onServiceDisconnected(ComponentName name) { + Log.i(TAG, "Service disconnected " + name); + } + }; + bindService(intent, conn, Context.BIND_AUTO_CREATE); + return true; + } + }); + menu.add("Start!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, SingleUserService.class); + startService(intent); + return true; + } + }); + menu.add("Send!").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(ActivityTestMain.this, UserTarget.class); + sendBroadcastToUser(intent, 1); + return true; + } + }); return true; } diff --git a/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java new file mode 100644 index 0000000..c40582a --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/SingleUserService.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 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 com.google.android.test.activity; + +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; + +public class SingleUserService extends Service { + Binder mBinder = new Binder(); + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } +} diff --git a/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java new file mode 100644 index 0000000..9890483 --- /dev/null +++ b/tests/ActivityTests/src/com/google/android/test/activity/UserTarget.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 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 com.google.android.test.activity; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class UserTarget extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.i("ActivityTest", "Received: " + intent); + } +} |