diff options
26 files changed, 514 insertions, 51 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 0639d7273dda..dac3481ea6dd 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9046,8 +9046,8 @@ package android.bluetooth { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_NAME_CHANGED = "android.bluetooth.device.action.NAME_CHANGED"; - field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; - field @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; field public static final int ADDRESS_TYPE_PUBLIC = 0; // 0x0 field public static final int ADDRESS_TYPE_RANDOM = 1; // 0x1 field public static final int BOND_BONDED = 12; // 0xc diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2a0dddc6d088..0113bd0f211f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1920,7 +1920,7 @@ package android.bluetooth { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); - field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; } public final class BluetoothAdapter { @@ -1972,7 +1972,7 @@ package android.bluetooth { field public static final int ACCESS_ALLOWED = 1; // 0x1 field public static final int ACCESS_REJECTED = 2; // 0x2 field public static final int ACCESS_UNKNOWN = 0; // 0x0 - field public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_SILENCE_MODE_CHANGED = "android.bluetooth.device.action.SILENCE_MODE_CHANGED"; field public static final String DEVICE_TYPE_DEFAULT = "Default"; field public static final String DEVICE_TYPE_UNTETHERED_HEADSET = "Untethered Headset"; field public static final String DEVICE_TYPE_WATCH = "Watch"; @@ -2034,7 +2034,7 @@ package android.bluetooth { method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); - field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; } public final class BluetoothMapClient implements android.bluetooth.BluetoothProfile { @@ -2063,7 +2063,7 @@ package android.bluetooth { public class BluetoothPbap implements android.bluetooth.BluetoothProfile { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int); - field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; + field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; } public interface BluetoothProfile { diff --git a/core/java/android/annotation/RequiresNoPermission.java b/core/java/android/annotation/RequiresNoPermission.java index 6ff4d6e34957..cdbf36ec20dd 100644 --- a/core/java/android/annotation/RequiresNoPermission.java +++ b/core/java/android/annotation/RequiresNoPermission.java @@ -27,7 +27,19 @@ import java.lang.annotation.Target; /** * Denotes that the annotated element requires no permissions. + * <p> + * This explicit annotation helps distinguish which of three states that an + * element may exist in: + * <ul> + * <li>Annotated with {@link RequiresPermission}, indicating that an element + * requires (or may require) one or more permissions. + * <li>Annotated with {@link RequiresNoPermission}, indicating that an element + * requires no permissions. + * <li>Neither annotation, indicating that no explicit declaration about + * permissions has been made for that element. + * </ul> * + * @see RequiresPermission * @hide */ @Retention(CLASS) diff --git a/core/java/android/annotation/RequiresPermission.java b/core/java/android/annotation/RequiresPermission.java index 303ab41bec8e..0379d303ab92 100644 --- a/core/java/android/annotation/RequiresPermission.java +++ b/core/java/android/annotation/RequiresPermission.java @@ -74,6 +74,7 @@ import java.lang.annotation.Target; * public static final String ACTION_CALL = "android.intent.action.CALL"; * }</pre> * + * @see RequiresNoPermission * @hide */ @Retention(CLASS) diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 0d21e09293b3..c2718621a378 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -104,6 +104,8 @@ public final class BluetoothA2dp implements BluetoothProfile { "android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AVRCP_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp.profile.action.AVRCP_CONNECTION_STATE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index 280e8bc8402b..cbbf4c97969e 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -19,8 +19,10 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; @@ -70,7 +72,9 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @SuppressLint("ActionValue") - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 052a7733c220..0442514d9c67 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -604,6 +604,7 @@ public final class BluetoothAdapter { * * @hide */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; @@ -618,6 +619,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLUETOOTH_ADDRESS_CHANGED = "android.bluetooth.adapter.action.BLUETOOTH_ADDRESS_CHANGED"; @@ -642,6 +646,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_CONNECTED = "android.bluetooth.adapter.action.BLE_ACL_CONNECTED"; @@ -656,6 +663,9 @@ public final class BluetoothAdapter { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BLE_ACL_DISCONNECTED = "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED"; diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java index 5148d5b431d7..cac676d7dc9a 100644 --- a/core/java/android/bluetooth/BluetoothAvrcpController.java +++ b/core/java/android/bluetooth/BluetoothAvrcpController.java @@ -17,7 +17,9 @@ package android.bluetooth; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.content.Context; @@ -62,6 +64,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED"; @@ -74,6 +77,9 @@ public final class BluetoothAvrcpController implements BluetoothProfile { * most recent player setting. </li> * </ul> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PLAYER_SETTING = "android.bluetooth.avrcp-controller.profile.action.PLAYER_SETTING"; diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 1201663d1d10..46ca50082330 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -389,6 +389,8 @@ public final class BluetoothDevice implements Parcelable { /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static final String ACTION_SDP_RECORD = @@ -663,13 +665,15 @@ public final class BluetoothDevice implements Parcelable { * <p> Always contains the extra field {@link #EXTRA_UUID} */ @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_UUID = "android.bluetooth.device.action.UUID"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MAS_INSTANCE = "android.bluetooth.device.action.MAS_INSTANCE"; @@ -693,28 +697,36 @@ public final class BluetoothDevice implements Parcelable { * Broadcast Action: This intent is used to broadcast PAIRING REQUEST */ @RequiresLegacyBluetoothAdminPermission - @RequiresBluetoothScanPermission - @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @UnsupportedAppUsage public static final String ACTION_PAIRING_CANCEL = "android.bluetooth.device.action.PAIRING_CANCEL"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_REQUEST = "android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_REPLY = "android.bluetooth.device.action.CONNECTION_ACCESS_REPLY"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_ACCESS_CANCEL = "android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL"; @@ -725,6 +737,8 @@ public final class BluetoothDevice implements Parcelable { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SystemApi public static final String ACTION_SILENCE_MODE_CHANGED = diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java index 09b0a80313f6..26e46573dd95 100644 --- a/core/java/android/bluetooth/BluetoothDevicePicker.java +++ b/core/java/android/bluetooth/BluetoothDevicePicker.java @@ -16,8 +16,10 @@ package android.bluetooth; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.bluetooth.annotations.RequiresBluetoothConnectPermission; /** * A helper to show a system "Device Picker" activity to the user. @@ -39,6 +41,8 @@ public interface BluetoothDevicePicker { * Selected {@link BluetoothDevice} is returned in extra data named * {@link BluetoothDevice#EXTRA_DEVICE}. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DEVICE_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; @@ -54,6 +58,8 @@ public interface BluetoothDevicePicker { * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent * come from */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH"; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index a1ece7fc825d..51ef3c284f47 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -310,6 +310,7 @@ public final class BluetoothHeadset implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_HF_INDICATORS_VALUE_CHANGED = "android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index eef42d1b2f34..840b4d3121b6 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -72,6 +74,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * booleans with value <code>true</code>, * and not supported ones are <strong>not</strong> being sent at all.</p> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; @@ -90,6 +95,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * it also includes {@link #EXTRA_AUDIO_WBS} * indicating wide band speech support.</p> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AUDIO_STATE_CHANGED = "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; @@ -106,6 +114,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_VOICE_RECOGNITION}, * {@link #EXTRA_IN_BAND_RING}</p> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_AG_EVENT = "android.bluetooth.headsetclient.profile.action.AG_EVENT"; @@ -117,6 +128,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * with value of {@link BluetoothHeadsetClientCall} instance, * representing actual call state.</p> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CALL_CHANGED = "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; @@ -127,6 +141,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value * when for example user started voice recognition from HF unit. */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_RESULT = "android.bluetooth.headsetclient.profile.action.RESULT"; @@ -138,6 +155,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * Vendor event can be a response to an vendor specific command or unsolicited. * */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; @@ -149,6 +169,9 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { * {@link #EXTRA_NUMBER}, * with a <code>String</code> value representing phone number.</p> */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LAST_VTAG = "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index bef4472b4a28..68a9d371d3f9 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -80,6 +80,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PROTOCOL_MODE_CHANGED = "android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED"; @@ -87,6 +89,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_HANDSHAKE = "android.bluetooth.input.profile.action.HANDSHAKE"; @@ -94,6 +98,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_REPORT = "android.bluetooth.input.profile.action.REPORT"; @@ -101,6 +107,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_VIRTUAL_UNPLUG_STATUS = "android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS"; @@ -108,6 +116,8 @@ public final class BluetoothHidHost implements BluetoothProfile { /** * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_IDLE_TIME_CHANGED = "android.bluetooth.input.profile.action.IDLE_TIME_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index 998fde02832d..a025d9b4d0cd 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -53,6 +55,9 @@ public final class BluetoothMap implements BluetoothProfile, AutoCloseable { /** @hide */ @SuppressLint("ActionValue") @SystemApi + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index f20b533af045..d72081c0bac7 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -20,8 +20,10 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.SdkConstant.SdkConstantType; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -50,16 +52,27 @@ public final class BluetoothMapClient implements BluetoothProfile { private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; /** @hide */ + @RequiresPermission(android.Manifest.permission.RECEIVE_SMS) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_RECEIVED = "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; /* Actions to be used for pending intents */ /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; /** @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; @@ -68,6 +81,9 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_READ_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED"; @@ -76,6 +92,9 @@ public final class BluetoothMapClient implements BluetoothProfile { * * @hide */ + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED = "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java index e41eb4f25810..ef6fddf54d61 100644 --- a/core/java/android/bluetooth/BluetoothPbap.java +++ b/core/java/android/bluetooth/BluetoothPbap.java @@ -89,7 +89,8 @@ public class BluetoothPbap implements BluetoothProfile { */ @SuppressLint("ActionValue") @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index 85b865076508..7f4863891efb 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -19,7 +19,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.content.Context; import android.os.Binder; @@ -41,6 +43,9 @@ public final class BluetoothPbapClient implements BluetoothProfile { private static final boolean DBG = false; private static final boolean VDBG = false; + @RequiresBluetoothConnectPermission + @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index 87da22cbb7f5..b86857f42f41 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -19,7 +19,9 @@ package android.bluetooth; import android.Manifest; import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; import android.annotation.SuppressLint; +import android.annotation.SdkConstant.SdkConstantType; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; import android.compat.annotation.UnsupportedAppUsage; @@ -70,6 +72,7 @@ public final class BluetoothSap implements BluetoothProfile { @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED"; diff --git a/errorprone/java/android/annotation/SdkConstant.java b/errorprone/java/android/annotation/SdkConstant.java new file mode 100644 index 000000000000..0a5318609847 --- /dev/null +++ b/errorprone/java/android/annotation/SdkConstant.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 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 java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates a constant field value should be exported to be used in the SDK tools. + * @hide + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.SOURCE) +public @interface SdkConstant { + public static enum SdkConstantType { + ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE; + } + + SdkConstantType value(); +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java index f54782d4eb77..d1e4309c365e 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java @@ -26,40 +26,49 @@ import static com.google.errorprone.matchers.Matchers.methodInvocation; import static com.google.errorprone.matchers.Matchers.methodIsNamed; import static com.google.errorprone.matchers.Matchers.staticMethod; +import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import com.google.auto.service.AutoService; +import com.google.common.base.Objects; import com.google.errorprone.BugPattern; import com.google.errorprone.VisitorState; import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher; import com.google.errorprone.matchers.Description; import com.google.errorprone.matchers.Matcher; import com.google.errorprone.util.ASTHelpers; +import com.sun.source.tree.AssignmentTree; import com.sun.source.tree.ClassTree; import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MemberSelectTree; import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.MethodTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; import com.sun.source.util.TreeScanner; import com.sun.tools.javac.code.Symbol; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Type.ClassType; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; import java.util.regex.Pattern; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Name; /** * Inspects both the client and server side of AIDL interfaces to ensure that @@ -71,9 +80,8 @@ import javax.lang.model.element.ExecutableElement; name = "AndroidFrameworkRequiresPermission", summary = "Verifies that @RequiresPermission annotations are consistent across AIDL", severity = WARNING) -public final class RequiresPermissionChecker extends BugChecker implements MethodTreeMatcher { - private static final String ANNOTATION_REQUIRES_PERMISSION = "RequiresPermission"; - +public final class RequiresPermissionChecker extends BugChecker + implements MethodTreeMatcher, MethodInvocationTreeMatcher { private static final Matcher<ExpressionTree> ENFORCE_VIA_CONTEXT = methodInvocation( instanceMethod() .onDescendantOf("android.content.Context") @@ -110,6 +118,18 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho private static final Matcher<ExpressionTree> RESTORE_CALL = methodInvocation(staticMethod() .onClass("android.os.Binder").withSignature("restoreCallingIdentity(long)")); + private static final Matcher<ExpressionTree> SEND_BROADCAST = methodInvocation( + instanceMethod() + .onDescendantOf("android.content.Context") + .withNameMatching(Pattern.compile("^send(Ordered|Sticky)?Broadcast.*$"))); + private static final Matcher<ExpressionTree> SEND_PENDING_INTENT = methodInvocation( + instanceMethod() + .onDescendantOf("android.app.PendingIntent") + .named("send")); + + private static final Matcher<ExpressionTree> INTENT_SET_ACTION = methodInvocation( + instanceMethod().onDescendantOf("android.content.Intent").named("setAction")); + @Override public Description matchMethod(MethodTree tree, VisitorState state) { // Ignore methods without an implementation @@ -166,7 +186,7 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho // to require; yell if we're too broad if (!actualPerm.containsAll(expectedPerm)) { return buildDescription(tree) - .setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm + .setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm + " but too wide; only invokes methods requiring " + actualPerm) .build(); } @@ -174,6 +194,27 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho return Description.NO_MATCH; } + @Override + public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { + if (SEND_BROADCAST.matches(tree, state)) { + final ParsedRequiresPermission sourcePerm = + parseBroadcastSourceRequiresPermission(tree, state); + final ParsedRequiresPermission targetPerm = + parseBroadcastTargetRequiresPermission(tree, state); + if (sourcePerm == null) { + return buildDescription(tree) + .setMessage("Failed to resolve broadcast intent action for validation") + .build(); + } else if (!Objects.equal(sourcePerm, targetPerm)) { + return buildDescription(tree) + .setMessage("Broadcast annotated " + sourcePerm + " but protected with " + + targetPerm) + .build(); + } + } + return Description.NO_MATCH; + } + static class ParsedRequiresPermission { final Set<String> allOf = new HashSet<>(); final Set<String> anyOf = new HashSet<>(); @@ -203,6 +244,16 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho } @Override + public boolean equals(Object obj) { + if (obj instanceof ParsedRequiresPermission) { + final ParsedRequiresPermission other = (ParsedRequiresPermission) obj; + return allOf.equals(other.allOf) && anyOf.equals(other.anyOf); + } else { + return false; + } + } + + @Override public String toString() { if (isEmpty()) { return "[none]"; @@ -215,37 +266,143 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho return res; } + public static ParsedRequiresPermission from(RequiresPermission perm) { + final ParsedRequiresPermission res = new ParsedRequiresPermission(); + res.addAll(perm); + return res; + } + public void addAll(ParsedRequiresPermission perm) { + if (perm == null) return; this.allOf.addAll(perm.allOf); this.anyOf.addAll(perm.anyOf); } - public void addAll(AnnotationMirror a) { - for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : a - .getElementValues().entrySet()) { - if (entry.getKey().getSimpleName().contentEquals("value")) { - maybeAdd(allOf, entry.getValue()); - } else if (entry.getKey().getSimpleName().contentEquals("allOf")) { - maybeAdd(allOf, entry.getValue()); - } else if (entry.getKey().getSimpleName().contentEquals("anyOf")) { - maybeAdd(anyOf, entry.getValue()); - } + public void addAll(RequiresPermission perm) { + if (perm == null) return; + if (!perm.value().isEmpty()) this.allOf.add(perm.value()); + if (perm.allOf() != null) this.allOf.addAll(Arrays.asList(perm.allOf())); + if (perm.anyOf() != null) this.anyOf.addAll(Arrays.asList(perm.anyOf())); + } + + public void addConstValue(Tree tree) { + final Object value = ASTHelpers.constValue(tree); + if (value != null) { + allOf.add(String.valueOf(value)); } } + } - private static void maybeAdd(Set<String> set, Object value) { - if (value instanceof AnnotationValue) { - maybeAdd(set, ((AnnotationValue) value).getValue()); - } else if (value instanceof String) { - set.add((String) value); - } else if (value instanceof Collection) { - for (Object o : (Collection) value) { - maybeAdd(set, o); - } - } else { - throw new RuntimeException(String.valueOf(value.getClass())); + private static ExpressionTree findArgumentByParameterName(MethodInvocationTree tree, + Predicate<String> paramName) { + final MethodSymbol sym = ASTHelpers.getSymbol(tree); + final List<VarSymbol> params = sym.getParameters(); + for (int i = 0; i < params.size(); i++) { + if (paramName.test(params.get(i).name.toString())) { + return tree.getArguments().get(i); } } + return null; + } + + private static Name resolveName(ExpressionTree tree) { + if (tree instanceof IdentifierTree) { + return ((IdentifierTree) tree).getName(); + } else if (tree instanceof MemberSelectTree) { + return resolveName(((MemberSelectTree) tree).getExpression()); + } else { + return null; + } + } + + private static ParsedRequiresPermission parseIntentAction(NewClassTree tree) { + final Optional<? extends ExpressionTree> arg = tree.getArguments().stream().findFirst(); + if (arg.isPresent()) { + return ParsedRequiresPermission.from( + ASTHelpers.getAnnotation(arg.get(), RequiresPermission.class)); + } else { + return null; + } + } + + private static ParsedRequiresPermission parseIntentAction(MethodInvocationTree tree) { + return ParsedRequiresPermission.from(ASTHelpers.getAnnotation( + tree.getArguments().get(0), RequiresPermission.class)); + } + + private static ParsedRequiresPermission parseBroadcastSourceRequiresPermission( + MethodInvocationTree methodTree, VisitorState state) { + final ExpressionTree arg = findArgumentByParameterName(methodTree, + (name) -> name.toLowerCase().contains("intent")); + if (arg instanceof IdentifierTree) { + final Name argName = ((IdentifierTree) arg).getName(); + final MethodTree method = state.findEnclosing(MethodTree.class); + final AtomicReference<ParsedRequiresPermission> res = new AtomicReference<>(); + method.accept(new TreeScanner<Void, Void>() { + private ParsedRequiresPermission last; + + @Override + public Void visitMethodInvocation(MethodInvocationTree tree, Void param) { + if (Objects.equal(methodTree, tree)) { + res.set(last); + } else { + final Name name = resolveName(tree.getMethodSelect()); + if (Objects.equal(argName, name) + && INTENT_SET_ACTION.matches(tree, state)) { + last = parseIntentAction(tree); + } + } + return super.visitMethodInvocation(tree, param); + } + + @Override + public Void visitAssignment(AssignmentTree tree, Void param) { + final Name name = resolveName(tree.getVariable()); + final Tree init = tree.getExpression(); + if (Objects.equal(argName, name) + && init instanceof NewClassTree) { + last = parseIntentAction((NewClassTree) init); + } + return super.visitAssignment(tree, param); + } + + @Override + public Void visitVariable(VariableTree tree, Void param) { + final Name name = tree.getName(); + final ExpressionTree init = tree.getInitializer(); + if (Objects.equal(argName, name) + && init instanceof NewClassTree) { + last = parseIntentAction((NewClassTree) init); + } + return super.visitVariable(tree, param); + } + }, null); + return res.get(); + } + return null; + } + + private static ParsedRequiresPermission parseBroadcastTargetRequiresPermission( + MethodInvocationTree tree, VisitorState state) { + final ExpressionTree arg = findArgumentByParameterName(tree, + (name) -> name.toLowerCase().contains("permission")); + final ParsedRequiresPermission res = new ParsedRequiresPermission(); + if (arg != null) { + arg.accept(new TreeScanner<Void, Void>() { + @Override + public Void visitIdentifier(IdentifierTree tree, Void param) { + res.addConstValue(tree); + return super.visitIdentifier(tree, param); + } + + @Override + public Void visitMemberSelect(MemberSelectTree tree, Void param) { + res.addConstValue(tree); + return super.visitMemberSelect(tree, param); + } + }, null); + } + return res; } private static ParsedRequiresPermission parseRequiresPermissionRecursively( @@ -276,12 +433,7 @@ public final class RequiresPermissionChecker extends BugChecker implements Metho final ParsedRequiresPermission res = new ParsedRequiresPermission(); for (MethodSymbol symbol : symbols) { - for (AnnotationMirror a : symbol.getAnnotationMirrors()) { - if (a.getAnnotationType().asElement().getSimpleName() - .contentEquals(ANNOTATION_REQUIRES_PERMISSION)) { - res.addAll(a); - } - } + res.addAll(symbol.getAnnotation(RequiresPermission.class)); } return res; } diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java index c8772306a59b..1679bb6abf9b 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/ContextUserIdCheckerTest.java @@ -38,6 +38,7 @@ public class ContextUserIdCheckerTest { compilationHelper .addSourceFile("/android/annotation/SystemService.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceFile("/android/foo/IFooService.java") .addSourceFile("/android/os/IInterface.java") .addSourceFile("/android/os/RemoteException.java") @@ -68,6 +69,7 @@ public class ContextUserIdCheckerTest { compilationHelper .addSourceFile("/android/annotation/SystemService.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceFile("/android/foo/IFooService.java") .addSourceFile("/android/os/IInterface.java") .addSourceFile("/android/os/UserHandle.java") @@ -98,6 +100,7 @@ public class ContextUserIdCheckerTest { compilationHelper .addSourceFile("/android/annotation/SystemService.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceFile("/android/foo/IFooService.java") .addSourceFile("/android/os/IInterface.java") .addSourceFile("/android/os/UserHandle.java") @@ -124,6 +127,7 @@ public class ContextUserIdCheckerTest { compilationHelper .addSourceFile("/android/annotation/SystemService.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceFile("/android/foo/IFooService.java") .addSourceFile("/android/os/IInterface.java") .addSourceFile("/android/os/UserHandle.java") diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java index 771258d7d265..388988e5e9bd 100644 --- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/RequiresPermissionCheckerTest.java @@ -81,6 +81,7 @@ public class RequiresPermissionCheckerTest { compilationHelper .addSourceFile("/android/annotation/RequiresPermission.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceLines("ColorManager.java", "import android.annotation.RequiresPermission;", "import android.content.Context;", @@ -139,6 +140,7 @@ public class RequiresPermissionCheckerTest { compilationHelper .addSourceFile("/android/annotation/RequiresPermission.java") .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") .addSourceFile("/android/foo/IColorService.java") .addSourceFile("/android/os/IInterface.java") .addSourceLines("ColorService.java", @@ -322,4 +324,95 @@ public class RequiresPermissionCheckerTest { "}") .doTest(); } + + @Test + public void testSendBroadcast() { + compilationHelper + .addSourceFile("/android/annotation/RequiresPermission.java") + .addSourceFile("/android/annotation/SdkConstant.java") + .addSourceFile("/android/content/Context.java") + .addSourceFile("/android/content/Intent.java") + .addSourceLines("FooManager.java", + "import android.annotation.RequiresPermission;", + "import android.annotation.SdkConstant;", + "import android.content.Context;", + "import android.content.Intent;", + "public class FooManager {", + " private static final String PERMISSION_RED = \"red\";", + " private static final String PERMISSION_BLUE = \"blue\";", + " @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)", + " private static final String ACTION_NONE = \"none\";", + " @RequiresPermission(PERMISSION_RED)", + " @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)", + " private static final String ACTION_RED = \"red\";", + " @RequiresPermission(allOf={PERMISSION_RED,PERMISSION_BLUE})", + " @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)", + " private static final String ACTION_RED_BLUE = \"red_blue\";", + " public void exampleNone(Context context) {", + " Intent intent = new Intent(ACTION_NONE);", + " context.sendBroadcast(intent);", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent, PERMISSION_RED);", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED });", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED, PERMISSION_BLUE });", + " }", + " public void exampleRed(Context context) {", + " Intent intent = new Intent(FooManager.ACTION_RED);", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent);", + " context.sendBroadcast(intent, FooManager.PERMISSION_RED);", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { FooManager.PERMISSION_RED });", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { FooManager.PERMISSION_RED, PERMISSION_BLUE });", + " }", + " public void exampleRedBlue(Context context) {", + " Intent intent = new Intent(ACTION_RED_BLUE);", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent);", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent, PERMISSION_RED);", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED });", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED, PERMISSION_BLUE });", + " }", + " public void exampleUnknown(Context context, Intent intent) {", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent);", + " // BUG: Diagnostic contains:", + " context.sendBroadcast(intent, PERMISSION_RED);", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED });", + " // BUG: Diagnostic contains:", + " context.sendBroadcastWithMultiplePermissions(intent,", + " new String[] { PERMISSION_RED, PERMISSION_BLUE });", + " }", + " public void exampleReuse(Context context) {", + " Intent intent = new Intent(ACTION_RED);", + " context.sendBroadcast(intent, PERMISSION_RED);", + " intent = new Intent(ACTION_NONE);", + " context.sendBroadcast(intent);", + " intent.setAction(ACTION_RED);", + " context.sendBroadcast(intent, PERMISSION_RED);", + " }", + " public void exampleScoped(Context context) {", + " if (true) {", + " Intent intent = new Intent(ACTION_RED);", + " context.sendBroadcast(intent, PERMISSION_RED);", + " } else {", + " Intent intent = new Intent(ACTION_NONE);", + " context.sendBroadcast(intent);", + " }", + " }", + "}") + .doTest(); + } } diff --git a/errorprone/tests/res/android/annotation/SdkConstant.java b/errorprone/tests/res/android/annotation/SdkConstant.java new file mode 100644 index 000000000000..0a5318609847 --- /dev/null +++ b/errorprone/tests/res/android/annotation/SdkConstant.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2008 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 java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Indicates a constant field value should be exported to be used in the SDK tools. + * @hide + */ +@Target({ ElementType.FIELD }) +@Retention(RetentionPolicy.SOURCE) +public @interface SdkConstant { + public static enum SdkConstantType { + ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE; + } + + SdkConstantType value(); +} diff --git a/errorprone/tests/res/android/content/Context.java b/errorprone/tests/res/android/content/Context.java index 323b8dd46e8f..efc4fb122435 100644 --- a/errorprone/tests/res/android/content/Context.java +++ b/errorprone/tests/res/android/content/Context.java @@ -18,9 +18,22 @@ package android.content; public class Context { public int getUserId() { - return 0; + throw new UnsupportedOperationException(); } public void enforceCallingOrSelfPermission(String permission, String message) { + throw new UnsupportedOperationException(); + } + + public void sendBroadcast(Intent intent) { + throw new UnsupportedOperationException(); + } + + public void sendBroadcast(Intent intent, String receiverPermission) { + throw new UnsupportedOperationException(); + } + + public void sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions) { + throw new UnsupportedOperationException(); } } diff --git a/errorprone/tests/res/android/content/Intent.java b/errorprone/tests/res/android/content/Intent.java index 9d22d04b8cb8..288396e60577 100644 --- a/errorprone/tests/res/android/content/Intent.java +++ b/errorprone/tests/res/android/content/Intent.java @@ -17,4 +17,11 @@ package android.content; public class Intent { + public Intent(String action) { + throw new UnsupportedOperationException(); + } + + public Intent setAction(String action) { + throw new UnsupportedOperationException(); + } } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index c3a5d1f871bb..a68103f68d51 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -2494,7 +2494,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } @RequiresPermission(allOf = { @@ -2577,7 +2577,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_CONNECT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } } |