diff options
| author | 2015-12-10 20:13:29 +0000 | |
|---|---|---|
| committer | 2015-12-10 20:13:29 +0000 | |
| commit | 0c6cc308cfd26ae102a1a3deb258e675da2c1eb0 (patch) | |
| tree | 1cae39c64e8f239fb491b0a478b73716cf7ad324 | |
| parent | 6ec3e4c56b4539e59b932137030e3e745cfdec00 (diff) | |
| parent | e0e413e2b17a0164e15c77f4ab51b3166f9111d2 (diff) | |
Merge "Add new target SDK filtering feature to BroadcastOptions."
10 files changed, 212 insertions, 63 deletions
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 1f378da4afd3..175b9799c5db 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.SystemApi; +import android.os.Build; import android.os.Bundle; /** @@ -28,15 +29,28 @@ import android.os.Bundle; @SystemApi public class BroadcastOptions { private long mTemporaryAppWhitelistDuration; + private int mMinManifestReceiverApiLevel = 0; + private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; /** * How long to temporarily put an app on the power whitelist when executing this broadcast * to it. - * @hide */ - public static final String KEY_TEMPORARY_APP_WHITELIST_DURATION + static final String KEY_TEMPORARY_APP_WHITELIST_DURATION = "android:broadcast.temporaryAppWhitelistDuration"; + /** + * Corresponds to {@link #setMinManifestReceiverApiLevel}. + */ + static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL + = "android:broadcast.minManifestReceiverApiLevel"; + + /** + * Corresponds to {@link #setMaxManifestReceiverApiLevel}. + */ + static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL + = "android:broadcast.maxManifestReceiverApiLevel"; + public static BroadcastOptions makeBasic() { BroadcastOptions opts = new BroadcastOptions(); return opts; @@ -48,6 +62,9 @@ public class BroadcastOptions { /** @hide */ public BroadcastOptions(Bundle opts) { mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION); + mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0); + mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, + Build.VERSION_CODES.CUR_DEVELOPMENT); } /** @@ -68,10 +85,46 @@ public class BroadcastOptions { } /** + * Set the minimum target API level of receivers of the broadcast. If an application + * is targeting an API level less than this, the broadcast will not be delivered to + * them. This only applies to receivers declared in the app's AndroidManifest.xml. + * @hide + */ + public void setMinManifestReceiverApiLevel(int apiLevel) { + mMinManifestReceiverApiLevel = apiLevel; + } + + /** + * Return {@link #setMinManifestReceiverApiLevel}. + * @hide + */ + public int getMinManifestReceiverApiLevel() { + return mMinManifestReceiverApiLevel; + } + + /** + * Set the maximum target API level of receivers of the broadcast. If an application + * is targeting an API level greater than this, the broadcast will not be delivered to + * them. This only applies to receivers declared in the app's AndroidManifest.xml. + * @hide + */ + public void setMaxManifestReceiverApiLevel(int apiLevel) { + mMaxManifestReceiverApiLevel = apiLevel; + } + + /** + * Return {@link #setMaxManifestReceiverApiLevel}. + * @hide + */ + public int getMaxManifestReceiverApiLevel() { + return mMaxManifestReceiverApiLevel; + } + + /** * Returns the created options as a Bundle, which can be passed to * {@link android.content.Context#sendBroadcast(android.content.Intent) * Context.sendBroadcast(Intent)} and related methods. - * Note that the returned Bundle is still owned by the ActivityOptions + * Note that the returned Bundle is still owned by the BroadcastOptions * object; you must not modify it, but can supply it to the sendBroadcast * methods that take an options Bundle. */ @@ -80,6 +133,12 @@ public class BroadcastOptions { if (mTemporaryAppWhitelistDuration > 0) { b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration); } + if (mMinManifestReceiverApiLevel != 0) { + b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel); + } + if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) { + b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel); + } return b.isEmpty() ? null : b; } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index d90ed9f6e639..569ab1116b5b 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1093,7 +1093,23 @@ class ContextImpl extends Context { intent.prepareToLeaveProcess(); ActivityManagerNative.getDefault().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, - Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier()); + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, + user.getIdentifier()); + } catch (RemoteException e) { + throw new RuntimeException("Failure from system", e); + } + } + + @Override + @Deprecated + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.prepareToLeaveProcess(); + ActivityManagerNative.getDefault().broadcastIntent( + mMainThread.getApplicationThread(), intent, resolvedType, null, + Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true, + user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c61f20444813..38a4475064af 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2116,6 +2116,14 @@ public abstract class Context { UserHandle user); /** + * @hide + * This is just here for sending CONNECTIVITY_ACTION. + */ + @Deprecated + public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent, + UserHandle user, Bundle options); + + /** * <p>Version of * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)} * that allows you to specify the diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index e49e771d4124..1a3d262f6b56 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -536,6 +536,13 @@ public class ContextWrapper extends Context { mBase.sendStickyBroadcastAsUser(intent, user); } + /** @hide */ + @Override + @Deprecated + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + mBase.sendStickyBroadcastAsUser(intent, user, options); + } + @Override @Deprecated public void sendStickyOrderedBroadcastAsUser(Intent intent, diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index c712a568c480..ed64c2b37a4a 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -34,6 +34,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import android.annotation.Nullable; import android.app.AlarmManager; +import android.app.BroadcastOptions; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -72,6 +73,7 @@ import android.net.RouteInfo; import android.net.UidRange; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; @@ -1529,6 +1531,7 @@ public class ConnectivityService extends IConnectivityManager.Stub log("sendStickyBroadcast: action=" + intent.getAction()); } + Bundle options = null; final long ident = Binder.clearCallingIdentity(); if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { final NetworkInfo ni = intent.getParcelableExtra( @@ -1536,6 +1539,10 @@ public class ConnectivityService extends IConnectivityManager.Stub if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) { intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + } else { + BroadcastOptions opts = BroadcastOptions.makeBasic(); + opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M); + options = opts.toBundle(); } final IBatteryStats bs = BatteryStatsService.getService(); try { @@ -1546,7 +1553,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } try { - mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 1bab7b99ec62..622aa16cb852 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -468,7 +468,7 @@ public final class BroadcastQueue { } private void deliverToRegisteredReceiverLocked(BroadcastRecord r, - BroadcastFilter filter, boolean ordered) { + BroadcastFilter filter, boolean ordered, int index) { boolean skip = false; if (filter.requiredPermission != null) { int perm = mService.checkComponentPermission(filter.requiredPermission, @@ -576,64 +576,70 @@ public final class BroadcastQueue { if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid, r.callingPid, r.resolvedType, filter.receiverList.uid)) { - return; + skip = true; } - if (filter.receiverList.app == null || filter.receiverList.app.crashing) { + if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) { Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r + " to " + filter.receiverList + ": process crashing"); skip = true; } - if (!skip) { - // If permissions need a review before any of the app components can run, we drop - // the broadcast and if the calling app is in the foreground and the broadcast is - // explicit we launch the review UI passing it a pending intent to send the skipped - // broadcast. - if (Build.PERMISSIONS_REVIEW_REQUIRED) { - if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName, - filter.owningUserId)) { - return; - } + if (skip) { + r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED; + return; + } + + // If permissions need a review before any of the app components can run, we drop + // the broadcast and if the calling app is in the foreground and the broadcast is + // explicit we launch the review UI passing it a pending intent to send the skipped + // broadcast. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName, + filter.owningUserId)) { + r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED; + return; } + } - // If this is not being sent as an ordered broadcast, then we - // don't want to touch the fields that keep track of the current - // state of ordered broadcasts. + r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED; + + // If this is not being sent as an ordered broadcast, then we + // don't want to touch the fields that keep track of the current + // state of ordered broadcasts. + if (ordered) { + r.receiver = filter.receiverList.receiver.asBinder(); + r.curFilter = filter; + filter.receiverList.curBroadcast = r; + r.state = BroadcastRecord.CALL_IN_RECEIVE; + if (filter.receiverList.app != null) { + // Bump hosting application to no longer be in background + // scheduling class. Note that we can't do that if there + // isn't an app... but we can only be in that case for + // things that directly call the IActivityManager API, which + // are already core system stuff so don't matter for this. + r.curApp = filter.receiverList.app; + filter.receiverList.app.curReceiver = r; + mService.updateOomAdjLocked(r.curApp); + } + } + try { + if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, + "Delivering to " + filter + " : " + r); + performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, + new Intent(r.intent), r.resultCode, r.resultData, + r.resultExtras, r.ordered, r.initialSticky, r.userId); if (ordered) { - r.receiver = filter.receiverList.receiver.asBinder(); - r.curFilter = filter; - filter.receiverList.curBroadcast = r; - r.state = BroadcastRecord.CALL_IN_RECEIVE; - if (filter.receiverList.app != null) { - // Bump hosting application to no longer be in background - // scheduling class. Note that we can't do that if there - // isn't an app... but we can only be in that case for - // things that directly call the IActivityManager API, which - // are already core system stuff so don't matter for this. - r.curApp = filter.receiverList.app; - filter.receiverList.app.curReceiver = r; - mService.updateOomAdjLocked(r.curApp); - } + r.state = BroadcastRecord.CALL_DONE_RECEIVE; } - try { - if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, - "Delivering to " + filter + " : " + r); - performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, - new Intent(r.intent), r.resultCode, r.resultData, - r.resultExtras, r.ordered, r.initialSticky, r.userId); - if (ordered) { - r.state = BroadcastRecord.CALL_DONE_RECEIVE; - } - } catch (RemoteException e) { - Slog.w(TAG, "Failure sending broadcast " + r.intent, e); - if (ordered) { - r.receiver = null; - r.curFilter = null; - filter.receiverList.curBroadcast = null; - if (filter.receiverList.app != null) { - filter.receiverList.app.curReceiver = null; - } + } catch (RemoteException e) { + Slog.w(TAG, "Failure sending broadcast " + r.intent, e); + if (ordered) { + r.receiver = null; + r.curFilter = null; + filter.receiverList.curBroadcast = null; + if (filter.receiverList.app != null) { + filter.receiverList.app.curReceiver = null; } } } @@ -740,7 +746,7 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Delivering non-ordered on [" + mQueueName + "] to registered " + target + ": " + r); - deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false); + deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast [" @@ -897,7 +903,7 @@ public final class BroadcastQueue { "Delivering ordered [" + mQueueName + "] to registered " + filter + ": " + r); - deliverToRegisteredReceiverLocked(r, filter, r.ordered); + deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); if (r.receiver == null || !r.ordered) { // The receiver has already finished, so schedule to // process the next one. @@ -925,10 +931,17 @@ public final class BroadcastQueue { info.activityInfo.name); boolean skip = false; + if (brOptions != null && + (info.activityInfo.applicationInfo.targetSdkVersion + < brOptions.getMinManifestReceiverApiLevel() || + info.activityInfo.applicationInfo.targetSdkVersion + > brOptions.getMaxManifestReceiverApiLevel())) { + skip = true; + } int perm = mService.checkComponentPermission(info.activityInfo.permission, r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid, info.activityInfo.exported); - if (perm != PackageManager.PERMISSION_GRANTED) { + if (!skip && perm != PackageManager.PERMISSION_GRANTED) { if (!info.activityInfo.exported) { Slog.w(TAG, "Permission Denial: broadcasting " + r.intent.toString() @@ -945,7 +958,7 @@ public final class BroadcastQueue { + " due to receiver " + component.flattenToShortString()); } skip = true; - } else if (info.activityInfo.permission != null) { + } else if (!skip && info.activityInfo.permission != null) { final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission); if (opCode != AppOpsManager.OP_NONE && mService.mAppOpsService.noteOperation(opCode, r.callingUid, @@ -1118,6 +1131,7 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " + r + " for whatever reason"); + r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED; r.receiver = null; r.curFilter = null; r.state = BroadcastRecord.IDLE; @@ -1125,6 +1139,7 @@ public final class BroadcastQueue { return; } + r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED; r.state = BroadcastRecord.APP_RECEIVE; r.curComponent = component; r.curReceiver = info.activityInfo; @@ -1294,6 +1309,7 @@ public final class BroadcastQueue { String anrMessage = null; Object curReceiver = r.receivers.get(r.nextReceiver-1); + r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT; Slog.w(TAG, "Receiver during timeout: " + curReceiver); logBroadcastReceiverDiscardLocked(r); if (curReceiver instanceof BroadcastFilter) { diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 1a269cf840f7..e99cbf9e563f 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -57,6 +57,7 @@ final class BroadcastRecord extends Binder { final int appOp; // an app op that is associated with this broadcast final BroadcastOptions options; // BroadcastOptions supplied by caller final List receivers; // contains BroadcastFilter and ResolveInfo + final int[] delivery; // delivery state of each receiver IIntentReceiver resultTo; // who receives final result if non-null long enqueueClockTime; // the clock time the broadcast was enqueued long dispatchTime; // when dispatch started on this set of receivers @@ -79,6 +80,11 @@ final class BroadcastRecord extends Binder { static final int CALL_DONE_RECEIVE = 3; static final int WAITING_SERVICES = 4; + static final int DELIVERY_PENDING = 0; + static final int DELIVERY_DELIVERED = 1; + static final int DELIVERY_SKIPPED = 2; + static final int DELIVERY_TIMEOUT = 3; + // The following are set when we are calling a receiver (one that // was found in our list of registered receivers). BroadcastFilter curFilter; @@ -183,12 +189,24 @@ final class BroadcastRecord extends Binder { PrintWriterPrinter printer = new PrintWriterPrinter(pw); for (int i = 0; i < N; i++) { Object o = receivers.get(i); - pw.print(prefix); pw.print("Receiver #"); pw.print(i); - pw.print(": "); pw.println(o); - if (o instanceof BroadcastFilter) - ((BroadcastFilter)o).dumpBrief(pw, p2); - else if (o instanceof ResolveInfo) - ((ResolveInfo)o).dump(printer, p2, 0); + pw.print(prefix); + switch (delivery[i]) { + case DELIVERY_PENDING: pw.print("Pending"); break; + case DELIVERY_DELIVERED: pw.print("Deliver"); break; + case DELIVERY_SKIPPED: pw.print("Skipped"); break; + case DELIVERY_TIMEOUT: pw.print("Timeout"); break; + default: pw.print("???????"); break; + } + pw.print(" #"); pw.print(i); pw.print(": "); + if (o instanceof BroadcastFilter) { + pw.println(o); + ((BroadcastFilter) o).dumpBrief(pw, p2); + } else if (o instanceof ResolveInfo) { + pw.println("(manifest)"); + ((ResolveInfo) o).dump(printer, p2, 0); + } else { + pw.println(o); + } } } @@ -211,6 +229,7 @@ final class BroadcastRecord extends Binder { appOp = _appOp; options = _options; receivers = _receivers; + delivery = new int[_receivers != null ? _receivers.size() : 0]; resultTo = _resultTo; resultCode = _resultCode; resultData = _resultData; diff --git a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java index 757f1c6ea39d..13657ab7f02d 100644 --- a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java +++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; +import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; @@ -165,6 +166,11 @@ public class BroadcastInterceptingContext extends ContextWrapper { } @Override + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + sendBroadcast(intent); + } + + @Override public void removeStickyBroadcast(Intent intent) { // ignored } diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index 96c818571df0..64d29787f75f 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -433,6 +433,12 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + throw new UnsupportedOperationException(); + } + @Override public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index b09a14fd1b39..63411b070d58 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1634,6 +1634,11 @@ public final class BridgeContext extends Context { } @Override + public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) { + // pass + } + + @Override public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, |