Create new BroadcastBehavior annotation.

This will be used to help document the expected behavior of various
broadcast actions defined by the OS.

Also add logic to PackageParser that will then yell at developers
whose manifests are making bad assumptions about which broadcasts
they'll receive.

Test: builds, boots
Bug: 35925551
Change-Id: I059c2bf8aa3ce53d9ff18dcc263db7620cd14bd6
This commit is contained in:
Jeff Sharkey
2017-03-08 20:17:51 -07:00
parent e6306c485f
commit 32ee8eefd7
6 changed files with 124 additions and 1 deletions

View File

@@ -21,8 +21,11 @@ import static android.Manifest.permission.GET_ACCOUNTS;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.Size;
import android.annotation.SystemApi;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.BroadcastBehavior;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -335,6 +338,8 @@ public class AccountManager {
*
* @deprecated use #addOnAccountsUpdatedListener to get account updates in runtime.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(includeBackground = true)
public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
"android.accounts.LOGIN_ACCOUNTS_CHANGED";

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 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.annotation;
import android.content.Intent;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Description of how the annotated broadcast action behaves.
*
* @hide
*/
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.SOURCE)
public @interface BroadcastBehavior {
/**
* This broadcast will only be delivered to an explicit target.
*
* @see Intent#setPackage(String)
* @see Intent#setComponent(android.content.ComponentName)
*/
boolean explicitOnly() default false;
/**
* This broadcast will only be delivered to registered receivers.
*
* @see Intent#FLAG_RECEIVER_REGISTERED_ONLY
*/
boolean registeredOnly() default false;
/**
* This broadcast will include all {@code AndroidManifest.xml} receivers
* regardless of process state.
*
* @see Intent#FLAG_RECEIVER_INCLUDE_BACKGROUND
*/
boolean includeBackground() default false;
}

View File

@@ -17,6 +17,7 @@
package android.app.admin;
import android.accounts.AccountManager;
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -81,6 +82,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* that other applications can not abuse it.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_DEVICE_ADMIN_ENABLED
= "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -94,6 +96,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* to the user before they disable your admin.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
= "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
@@ -115,6 +118,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* its intent filter.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_DEVICE_ADMIN_DISABLED
= "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -131,6 +135,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* this broadcast.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_PASSWORD_CHANGED
= "android.app.action.ACTION_PASSWORD_CHANGED";
@@ -147,6 +152,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* this broadcast.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_PASSWORD_FAILED
= "android.app.action.ACTION_PASSWORD_FAILED";
@@ -160,6 +166,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* this broadcast.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_PASSWORD_SUCCEEDED
= "android.app.action.ACTION_PASSWORD_SUCCEEDED";
@@ -173,6 +180,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* this broadcast.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_PASSWORD_EXPIRING
= "android.app.action.ACTION_PASSWORD_EXPIRING";
@@ -187,6 +195,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @see DevicePolicyManager#isLockTaskPermitted(String)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_LOCK_TASK_ENTERING
= "android.app.action.LOCK_TASK_ENTERING";
@@ -200,6 +209,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @see DevicePolicyManager#isLockTaskPermitted(String)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_LOCK_TASK_EXITING
= "android.app.action.LOCK_TASK_EXITING";
@@ -232,6 +242,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* <p>Output: Nothing</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
"android.app.action.PROFILE_PROVISIONING_COMPLETE";
@@ -244,6 +255,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_BUGREPORT_SHARING_DECLINED =
"android.app.action.BUGREPORT_SHARING_DECLINED";
@@ -256,6 +268,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_BUGREPORT_FAILED = "android.app.action.BUGREPORT_FAILED";
/**
@@ -266,6 +279,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_BUGREPORT_SHARE =
"android.app.action.BUGREPORT_SHARE";
@@ -274,6 +288,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_SECURITY_LOGS_AVAILABLE
= "android.app.action.SECURITY_LOGS_AVAILABLE";
@@ -283,6 +298,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_NETWORK_LOGS_AVAILABLE
= "android.app.action.NETWORK_LOGS_AVAILABLE";
@@ -314,7 +330,8 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_USER_ADDED = "android.app.action.USER_ADDED";
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_USER_ADDED = "android.app.action.USER_ADDED";
/**
* Broadcast action: notify the device owner that a user or profile has been removed.
@@ -323,6 +340,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_USER_REMOVED = "android.app.action.USER_REMOVED";
/**
@@ -401,6 +419,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE =
"android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";

View File

@@ -16,8 +16,11 @@
package android.appwidget;
import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -81,12 +84,14 @@ public class AppWidgetManager {
*
* @see #ACTION_APPWIDGET_CONFIGURE
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
/**
* Similar to ACTION_APPWIDGET_PICK, but used from keyguard
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String
ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK";
@@ -133,6 +138,7 @@ public class AppWidgetManager {
* @see #ACTION_APPWIDGET_CONFIGURE
*
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
/**
@@ -157,6 +163,7 @@ public class AppWidgetManager {
* and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED}
* broadcast.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
/**
@@ -290,6 +297,8 @@ public class AppWidgetManager {
*
* @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
/**
@@ -302,6 +311,8 @@ public class AppWidgetManager {
* AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
* AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
/**
@@ -312,6 +323,8 @@ public class AppWidgetManager {
*
* @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
/**
@@ -322,6 +335,8 @@ public class AppWidgetManager {
*
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
/**
@@ -334,6 +349,8 @@ public class AppWidgetManager {
*
* @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
/**
@@ -365,6 +382,8 @@ public class AppWidgetManager {
*
* @see #ACTION_APPWIDGET_HOST_RESTORED
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_RESTORED
= "android.appwidget.action.APPWIDGET_RESTORED";
@@ -402,6 +421,8 @@ public class AppWidgetManager {
*
* @see #ACTION_APPWIDGET_RESTORED
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(explicitOnly = true)
public static final String ACTION_APPWIDGET_HOST_RESTORED
= "android.appwidget.action.APPWIDGET_HOST_RESTORED";

View File

@@ -17,6 +17,7 @@
package android.content;
import android.annotation.AnyRes;
import android.annotation.BroadcastBehavior;
import android.annotation.IntDef;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -1991,6 +1992,7 @@ public class Intent implements Parcelable, Cloneable {
* This is a protected intent that can only be sent by the system.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@BroadcastBehavior(includeBackground = true)
public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
/**

View File

@@ -211,6 +211,14 @@ public class PackageParser {
CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
}
private static final boolean LOG_UNSAFE_BROADCASTS = false;
// Set of broadcast actions that are safe for manifest receivers
private static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
static {
SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
}
/** @hide */
public static class NewPermissionInfo {
public final String name;
@@ -4240,6 +4248,18 @@ public class PackageParser {
if (intent.isVisibleToInstantApp()) {
a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
}
if (LOG_UNSAFE_BROADCASTS && receiver
&& (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O)) {
for (int i = 0; i < intent.countActions(); i++) {
final String action = intent.getAction(i);
if (action == null || !action.startsWith("android.")) continue;
if (!SAFE_BROADCASTS.contains(action)) {
Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
+ owner.packageName + " as requested at: "
+ parser.getPositionDescription());
}
}
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,