Merge "Should not turn on validation for data"
diff --git a/api/current.txt b/api/current.txt
index 92d5670..587974c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -53562,6 +53562,7 @@
method public int mapIntFlag(@NonNull String, @AttrRes int, @NonNull android.view.inspector.IntFlagMapping);
method public int mapLong(@NonNull String, @AttrRes int);
method public int mapObject(@NonNull String, @AttrRes int);
+ method public int mapResourceId(@NonNull String, @AttrRes int);
method public int mapShort(@NonNull String, @AttrRes int);
}
@@ -53584,6 +53585,7 @@
method public void readIntFlag(int, int);
method public void readLong(int, long);
method public void readObject(int, @Nullable Object);
+ method public void readResourceId(int, @AnyRes int);
method public void readShort(int, short);
}
@@ -56242,7 +56244,7 @@
field public static final int SOURCE_BOUND_MAX_VISIBLE = 1; // 0x1
}
- public static class Magnifier.Builder {
+ public static final class Magnifier.Builder {
ctor public Magnifier.Builder(@NonNull android.view.View);
method @NonNull public android.widget.Magnifier build();
method @NonNull public android.widget.Magnifier.Builder setClippingEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2c9b7aa..cf40ec3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3539,6 +3539,7 @@
}
public class HwAudioSource {
+ method public boolean isPlaying();
method public void start();
method public void stop();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index c070171..11cf936 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3061,6 +3061,7 @@
enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_ENUM;
enum_constant public static final android.view.inspector.InspectableProperty.ValueType INT_FLAG;
enum_constant public static final android.view.inspector.InspectableProperty.ValueType NONE;
+ enum_constant public static final android.view.inspector.InspectableProperty.ValueType RESOURCE_ID;
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5968fa8..993f97d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3005,6 +3005,9 @@
// The elapsed real time of start of the process.
optional int64 process_start_time_nanos = 9;
+
+ // Min oom adj score considered by lmkd.
+ optional int32 min_oom_score = 10;
}
/*
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 56d22d0..ceb21e7 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -68,7 +68,14 @@
@Retention(SOURCE)
@IntDef({SOURCE_FROM_APP, SOURCE_FROM_ASSISTANT})
public @interface Source {}
+
+ /**
+ * To indicate an adjustment is from an app.
+ */
public static final int SOURCE_FROM_APP = 0;
+ /**
+ * To indicate an adjustment is from a {@link NotificationAssistantService}.
+ */
public static final int SOURCE_FROM_ASSISTANT = 1;
/**
diff --git a/core/java/android/view/inspector/InspectableProperty.java b/core/java/android/view/inspector/InspectableProperty.java
index 30938c1..97ede86 100644
--- a/core/java/android/view/inspector/InspectableProperty.java
+++ b/core/java/android/view/inspector/InspectableProperty.java
@@ -233,6 +233,17 @@
* @hide
*/
@TestApi
- GRAVITY
+ GRAVITY,
+
+ /**
+ * Value is a resource ID
+ *
+ * This type is inferred from the presence of a resource ID annotation such as
+ * {@link android.annotation.AnyRes}.
+ *
+ * @hide
+ */
+ @TestApi
+ RESOURCE_ID
}
}
diff --git a/core/java/android/view/inspector/PropertyMapper.java b/core/java/android/view/inspector/PropertyMapper.java
index 00b18d1..54d99df 100644
--- a/core/java/android/view/inspector/PropertyMapper.java
+++ b/core/java/android/view/inspector/PropertyMapper.java
@@ -157,6 +157,16 @@
@NonNull IntEnumMapping mapping);
/**
+ * Map a string name to an integer ID for an attribute that contains resource IDs.
+ *
+ * @param name The name of the property
+ * @param attributeId If the property is from an XML attribute, the resource ID of the property
+ * @return An integer ID for the property
+ * @throws PropertyConflictException If the property name is already mapped as another type.
+ */
+ int mapResourceId(@NonNull String name, @AttrRes int attributeId);
+
+ /**
* Map a string name to an integer ID for a flag set packed into an int property.
*
* @param name The name of the property
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index a8b7ecc..b5020ce 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -16,6 +16,7 @@
package android.view.inspector;
+import android.annotation.AnyRes;
import android.annotation.ColorInt;
import android.annotation.ColorLong;
import android.annotation.NonNull;
@@ -150,7 +151,7 @@
void readColor(int id, @Nullable Color value);
/**
- * Read {@link android.view.Gravity} packed into an primitive {int}.
+ * Read {@link android.view.Gravity} packed into an primitive {@code int}.
*
* @param id Identifier of the property from a {@link PropertyMapper}
* @param value Value of the property
@@ -159,7 +160,7 @@
void readGravity(int id, int value);
/**
- * Read an enumeration packed into a primitive {int}.
+ * Read an enumeration packed into a primitive {@code int}.
*
* @param id Identifier of the property from a {@link PropertyMapper}
* @param value Value of the property
@@ -168,7 +169,7 @@
void readIntEnum(int id, int value);
/**
- * Read a flag packed into a primitive {int}.
+ * Read a flag packed into a primitive {@code int}.
*
* @param id Identifier of the property from a {@link PropertyMapper}
* @param value Value of the property
@@ -177,6 +178,15 @@
void readIntFlag(int id, int value);
/**
+ * Read an integer that contains a resource ID.
+ *
+ * @param id Identifier of the property from a {@link PropertyMapper}
+ * @param value Value of the property
+ * @throws PropertyTypeMismatchException If the property ID is not mapped as a resource ID.
+ */
+ void readResourceId(int id, @AnyRes int value);
+
+ /**
* Thrown if a client calls a typed read method for a property of a different type.
*/
class PropertyTypeMismatchException extends RuntimeException {
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index b7cdad2..50e8836 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1116,7 +1116,7 @@
/**
* Builder class for {@link Magnifier} objects.
*/
- public static class Builder {
+ public static final class Builder {
private @NonNull View mView;
private @Px @IntRange(from = 0) int mWidth;
private @Px @IntRange(from = 0) int mHeight;
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index cc7daa8..db2f8d0 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -19,21 +19,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- CDMA home system id for Verizon -->
- <string-array translatable="false" name="config_cdma_home_system">
- <item>64</item>
- <item>65</item>
- <item>66</item>
- <item>76</item>
- <item>77</item>
- <item>78</item>
- <item>79</item>
- <item>80</item>
- <item>81</item>
- <item>82</item>
- <item>83</item>
- </string-array>
-
<!-- Flag indicating whether the IMS service can be turned off. If false then
the service will not be turned-off completely (the ImsManager.turnOffIms() will
be disabled) but individual Features can be disabled using ImsConfig.setFeatureValue() -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2f2fdca..22f7c2b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2917,14 +2917,6 @@
<bool name="config_networkSamplingWakesDevice">true</bool>
- <!-- Home (non-roaming) values for CDMA roaming indicator.
- Carriers can override this table by resource overlay. If not,
- the default values come from 3GPP2 C.R1001 table
- 8.1-1. Enhanced Roaming Indicator Number Assignments -->
- <string-array translatable="false" name="config_cdma_home_system">
- <item>1</item>
- </string-array>
-
<!--From SmsMessage-->
<!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
string that's stored in 8-bit unpacked format) characters.-->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d5444c0..ba8b054 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2606,7 +2606,6 @@
<java-symbol type="attr" name="lightRadius" />
<java-symbol type="attr" name="ambientShadowAlpha" />
<java-symbol type="attr" name="spotShadowAlpha" />
- <java-symbol type="array" name="config_cdma_home_system" />
<java-symbol type="bool" name="config_sms_decode_gsm_8bit_data" />
<java-symbol type="dimen" name="text_size_small_material" />
<java-symbol type="attr" name="checkMarkGravity" />
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index d9be0a5..e53b7e8 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -129,6 +129,7 @@
* Starts the playback from {@link AudioDeviceInfo}.
*/
public void start() {
+ Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing");
baseStart();
mNativeHandle = AudioSystem.startAudioSource(
mAudioDeviceInfo.getPort().activeConfig(),
@@ -136,6 +137,14 @@
}
/**
+ * Checks whether the HwAudioSource player is playing.
+ * @return true if currently playing, false otherwise
+ */
+ public boolean isPlaying() {
+ return mNativeHandle != 0;
+ }
+
+ /**
* Stops the playback from {@link AudioDeviceInfo}.
*/
public void stop() {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f5d58d8..f4c7275 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -938,7 +938,7 @@
<!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
<string name="power_discharging_duration_enhanced">About <xliff:g id="time_remaining">%1$s</xliff:g> left based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time_remaining">%1$s</xliff:g> left</string>
+ <string name="power_remaining_duration_only_short"><xliff:g id="time_remaining">%1$s</xliff:g></string>
<!-- [CHAR_LIMIT=100] Label for enhanced estimated time that phone will run out of battery -->
<string name="power_discharge_by_enhanced">Should last until about <xliff:g id="time">%1$s</xliff:g> based on your usage (<xliff:g id="level">%2$s</xliff:g>)</string>
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0a68ce0..1fd9fba 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18479,7 +18479,12 @@
public int checkPermission(String permName, String pkgName, int userId,
TriFunction<String, String, Integer, Integer> superImpl) {
if (mTargetPackageName.equals(pkgName) && isTargetPermission(permName)) {
- return superImpl.apply(permName, "com.android.shell", userId);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(permName, "com.android.shell", userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
return superImpl.apply(permName, pkgName, userId);
}
@@ -18488,7 +18493,12 @@
public int checkUidPermission(String permName, int uid,
BiFunction<String, Integer, Integer> superImpl) {
if (uid == mTargetUid && isTargetPermission(permName)) {
- return superImpl.apply(permName, Process.SHELL_UID);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return superImpl.apply(permName, Process.SHELL_UID);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
return superImpl.apply(permName, uid);
}
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 0b38ef9..b0b1840 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -617,11 +617,9 @@
* Cancel all current deferrals; that is, make all currently-deferred broadcasts
* immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
*/
- public void cancelDeferrals() {
- synchronized (mLock) {
- zeroDeferralTimes(mAlarmBroadcasts);
- zeroDeferralTimes(mDeferredBroadcasts);
- }
+ public void cancelDeferralsLocked() {
+ zeroDeferralTimes(mAlarmBroadcasts);
+ zeroDeferralTimes(mDeferredBroadcasts);
}
private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index c1ed54e..70733ef 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1870,7 +1870,10 @@
// Used by wait-for-broadcast-idle : fast-forward all current deferrals to
// be immediately deliverable.
void cancelDeferrals() {
- mDispatcher.cancelDeferrals();
+ synchronized (mService) {
+ mDispatcher.cancelDeferralsLocked();
+ scheduleBroadcastsLocked();
+ }
}
String describeState() {
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 52707c2..a91fbe7 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -763,6 +763,14 @@
noticed[clazz.fullname] = hash(clazz)
+verifiers = {}
+
+def verifier(f):
+ verifiers[f.__name__] = f
+ return f
+
+
+@verifier
def verify_constants(clazz):
"""All static final constants must be FOO_NAME style."""
if re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -780,13 +788,13 @@
if f.typ in req and f.value is None:
error(clazz, f, None, "All constants must be defined at compile time")
-
+@verifier
def verify_enums(clazz):
"""Enums are bad, mmkay?"""
if clazz.extends == "java.lang.Enum" or "enum" in clazz.split:
error(clazz, None, "F5", "Enums are not allowed")
-
+@verifier
def verify_class_names(clazz):
"""Try catching malformed class names like myMtp or MTPUser."""
if clazz.fullname.startswith("android.opengl"): return
@@ -801,6 +809,7 @@
error(clazz, None, None, "Don't expose your implementation details")
+@verifier
def verify_method_names(clazz):
"""Try catching malformed method names, like Foo() or getMTU()."""
if clazz.fullname.startswith("android.opengl"): return
@@ -814,9 +823,9 @@
error(clazz, m, "S1", "Method name must start with lowercase char")
+@verifier
def verify_callbacks(clazz):
"""Verify Callback classes.
- All callback classes must be abstract.
All methods must follow onFoo() naming style."""
if clazz.fullname == "android.speech.tts.SynthesisCallback": return
@@ -826,14 +835,12 @@
warn(clazz, None, "L1", "Class should be named FooCallback")
if clazz.name.endswith("Callback"):
- if "interface" in clazz.split:
- error(clazz, None, "CL3", "Callbacks must be abstract class to enable extension in future API levels")
-
for m in clazz.methods:
if not re.match("on[A-Z][a-z]*", m.name):
error(clazz, m, "L1", "Callback method names must be onFoo() style")
+@verifier
def verify_listeners(clazz):
"""Verify Listener classes.
All Listener classes must be interface.
@@ -855,6 +862,7 @@
error(clazz, m, "L1", "Single listener method name must match class name")
+@verifier
def verify_actions(clazz):
"""Verify intent actions.
All action names must be named ACTION_FOO.
@@ -886,6 +894,7 @@
error(clazz, f, "C4", "Inconsistent action value; expected '%s'" % (expected))
+@verifier
def verify_extras(clazz):
"""Verify intent extras.
All extra names must be named EXTRA_FOO.
@@ -916,6 +925,7 @@
error(clazz, f, "C4", "Inconsistent extra value; expected '%s'" % (expected))
+@verifier
def verify_equals(clazz):
"""Verify that equals() and hashCode() must be overridden together."""
eq = False
@@ -928,6 +938,7 @@
error(clazz, None, "M8", "Must override both equals and hashCode; missing one")
+@verifier
def verify_parcelable(clazz):
"""Verify that Parcelable objects aren't hiding required bits."""
if clazz.implements == "android.os.Parcelable":
@@ -946,6 +957,7 @@
error(clazz, c, "FW3", "Parcelable inflation is exposed through CREATOR, not raw constructors")
+@verifier
def verify_protected(clazz):
"""Verify that no protected methods or fields are allowed."""
for m in clazz.methods:
@@ -957,6 +969,7 @@
error(clazz, f, "M7", "Protected fields not allowed; must be public")
+@verifier
def verify_fields(clazz):
"""Verify that all exposed fields are final.
Exposed fields must follow myName style.
@@ -1002,6 +1015,7 @@
error(clazz, f, "C2", "Constants must be marked static final")
+@verifier
def verify_register(clazz):
"""Verify parity of registration methods.
Callback objects use register/unregister methods.
@@ -1035,6 +1049,7 @@
error(clazz, m, "L3", "Listener methods should be named add/remove")
+@verifier
def verify_sync(clazz):
"""Verify synchronized methods aren't exposed."""
for m in clazz.methods:
@@ -1042,6 +1057,7 @@
error(clazz, m, "M5", "Internal locks must not be exposed")
+@verifier
def verify_intent_builder(clazz):
"""Verify that Intent builders are createFooIntent() style."""
if clazz.name == "Intent": return
@@ -1054,6 +1070,7 @@
warn(clazz, m, "FW1", "Methods creating an Intent should be named createFooIntent()")
+@verifier
def verify_helper_classes(clazz):
"""Verify that helper classes are named consistently with what they extend.
All developer extendable methods should be named onFoo()."""
@@ -1102,6 +1119,7 @@
warn(clazz, m, None, "If implemented by developer, should be named onFoo(); otherwise consider marking final")
+@verifier
def verify_builder(clazz):
"""Verify builder classes.
Methods should return the builder to enable chaining."""
@@ -1134,12 +1152,14 @@
error(clazz, None, None, "Builder should be final")
+@verifier
def verify_aidl(clazz):
"""Catch people exposing raw AIDL."""
if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface":
error(clazz, None, None, "Raw AIDL interfaces must not be exposed")
+@verifier
def verify_internal(clazz):
"""Catch people exposing internal classes."""
if clazz.pkg.name.startswith("com.android"):
@@ -1174,6 +1194,7 @@
"android.util"
])
+@verifier
def verify_layering(clazz):
"""Catch package layering violations.
For example, something in android.os depending on android.app."""
@@ -1208,6 +1229,7 @@
warn(clazz, m, "FW6", "Method argument type violates package layering")
+@verifier
def verify_boolean(clazz):
"""Verifies that boolean accessors are named correctly.
For example, hasFoo() and setHasFoo()."""
@@ -1248,6 +1270,7 @@
error_if_exists(sets, m.name, expected, "has" + target)
+@verifier
def verify_collections(clazz):
"""Verifies that collection types are interfaces."""
if clazz.fullname == "android.os.Bundle": return
@@ -1263,6 +1286,23 @@
error(clazz, m, "CL2", "Argument is concrete collection; must be higher-level interface")
+@verifier
+def verify_uris(clazz):
+ bad = ["java.net.URL", "java.net.URI", "android.net.URL"]
+
+ for f in clazz.fields:
+ if f.typ in bad:
+ error(clazz, f, None, "Field must be android.net.Uri instead of " + f.typ)
+
+ for m in clazz.methods + clazz.ctors:
+ if m.typ in bad:
+ error(clazz, m, None, "Must return android.net.Uri instead of " + m.typ)
+ for arg in m.args:
+ if arg in bad:
+ error(clazz, m, None, "Argument must take android.net.Uri instead of " + arg)
+
+
+@verifier
def verify_flags(clazz):
"""Verifies that flags are non-overlapping."""
known = collections.defaultdict(int)
@@ -1279,6 +1319,7 @@
known[scope] |= val
+@verifier
def verify_exception(clazz):
"""Verifies that methods don't throw generic exceptions."""
for m in clazz.methods:
@@ -1296,8 +1337,10 @@
if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]:
warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException")
+
GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE)
+# Not marked as @verifier, because it is only conditionally applied.
def verify_google(clazz):
"""Verifies that APIs never reference Google."""
@@ -1310,6 +1353,7 @@
error(clazz, t, None, "Must never reference Google")
+@verifier
def verify_bitset(clazz):
"""Verifies that we avoid using heavy BitSet."""
@@ -1325,6 +1369,7 @@
error(clazz, m, None, "Argument type must not be heavy BitSet")
+@verifier
def verify_manager(clazz):
"""Verifies that FooManager is only obtained from Context."""
@@ -1338,6 +1383,7 @@
error(clazz, m, None, "Managers must always be obtained from Context")
+@verifier
def verify_boxed(clazz):
"""Verifies that methods avoid boxed primitives."""
@@ -1360,6 +1406,7 @@
error(clazz, m, "M11", "Must avoid boxed primitives")
+@verifier
def verify_static_utils(clazz):
"""Verifies that helper classes can't be constructed."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1379,6 +1426,7 @@
error(clazz, None, None, "Fully-static utility classes must not have constructor")
+# @verifier # Disabled for now
def verify_overload_args(clazz):
"""Verifies that method overloads add new arguments at the end."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1419,6 +1467,7 @@
error(clazz, m, "M2", "Expected consistent argument ordering between overloads: %s..." % (", ".join(locked_sig)))
+@verifier
def verify_callback_handlers(clazz):
"""Verifies that methods adding listener/callback have overload
for specifying delivery thread."""
@@ -1470,6 +1519,7 @@
warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Executor")
+@verifier
def verify_context_first(clazz):
"""Verifies that methods accepting a Context keep it the first argument."""
examine = clazz.ctors + clazz.methods
@@ -1482,6 +1532,7 @@
error(clazz, m, "M3", "ContentResolver is distinct, so it must be the first argument")
+@verifier
def verify_listener_last(clazz):
"""Verifies that methods accepting a Listener or Callback keep them as last arguments."""
examine = clazz.ctors + clazz.methods
@@ -1495,6 +1546,7 @@
warn(clazz, m, "M3", "Listeners should always be at end of argument list")
+@verifier
def verify_resource_names(clazz):
"""Verifies that resource names have consistent case."""
if not re.match("android\.R\.[a-z]+", clazz.fullname): return
@@ -1526,6 +1578,7 @@
error(clazz, f, "C7", "Expected resource name in this class to be FooBar_Baz style")
+@verifier
def verify_files(clazz):
"""Verifies that methods accepting File also accept streams."""
@@ -1547,6 +1600,7 @@
warn(clazz, m, "M10", "Methods accepting File should also accept FileDescriptor or streams")
+@verifier
def verify_manager_list(clazz):
"""Verifies that managers return List<? extends Parcelable> instead of arrays."""
@@ -1557,6 +1611,7 @@
warn(clazz, m, None, "Methods should return List<? extends Parcelable> instead of Parcelable[] to support ParceledListSlice under the hood")
+@verifier
def verify_abstract_inner(clazz):
"""Verifies that abstract inner classes are static."""
@@ -1565,6 +1620,7 @@
warn(clazz, None, None, "Abstract inner classes should be static to improve testability")
+@verifier
def verify_runtime_exceptions(clazz):
"""Verifies that runtime exceptions aren't listed in throws."""
@@ -1609,6 +1665,7 @@
error(clazz, m, None, "Methods must not mention RuntimeException subclasses in throws clauses")
+@verifier
def verify_error(clazz):
"""Verifies that we always use Exception instead of Error."""
if not clazz.extends: return
@@ -1618,6 +1675,7 @@
error(clazz, None, None, "Exceptions must be named FooException")
+@verifier
def verify_units(clazz):
"""Verifies that we use consistent naming for units."""
@@ -1654,6 +1712,7 @@
error(clazz, m, None, "Percentage must use ints")
+@verifier
def verify_closable(clazz):
"""Verifies that classes are AutoClosable."""
if "java.lang.AutoCloseable" in clazz.implements_all: return
@@ -1666,6 +1725,7 @@
return
+@verifier
def verify_member_name_not_kotlin_keyword(clazz):
"""Prevent method names which are keywords in Kotlin."""
@@ -1691,6 +1751,7 @@
error(clazz, f, None, "Field name must not be a Kotlin keyword")
+@verifier
def verify_method_name_not_kotlin_operator(clazz):
"""Warn about method names which become operators in Kotlin."""
@@ -1740,6 +1801,7 @@
unique_binary_op(m, m.name[:-6]) # Remove 'Assign' suffix
+@verifier
def verify_collections_over_arrays(clazz):
"""Warn that [] should be Collections."""
@@ -1755,6 +1817,7 @@
warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array")
+@verifier
def verify_user_handle(clazz):
"""Methods taking UserHandle should be ForUser or AsUser."""
if clazz.name.endswith("Listener") or clazz.name.endswith("Callback") or clazz.name.endswith("Callbacks"): return
@@ -1779,6 +1842,7 @@
"or 'queryFooForUser'")
+@verifier
def verify_params(clazz):
"""Parameter classes should be 'Params'."""
if clazz.name.endswith("Params"): return
@@ -1794,6 +1858,7 @@
error(clazz, None, None, "Classes holding a set of parameters should be called 'FooParams'")
+@verifier
def verify_services(clazz):
"""Service name should be FOO_BAR_SERVICE = 'foo_bar'."""
if clazz.fullname != "android.content.Context": return
@@ -1807,6 +1872,7 @@
error(clazz, f, "C4", "Inconsistent service value; expected '%s'" % (expected))
+@verifier
def verify_tense(clazz):
"""Verify tenses of method names."""
if clazz.fullname.startswith("android.opengl"): return
@@ -1816,6 +1882,7 @@
warn(clazz, m, None, "Unexpected tense; probably meant 'enabled'")
+@verifier
def verify_icu(clazz):
"""Verifies that richer ICU replacements are used."""
better = {
@@ -1847,6 +1914,7 @@
warn(clazz, m, None, "Type %s should be replaced with richer ICU type %s" % (arg, better[arg]))
+@verifier
def verify_clone(clazz):
"""Verify that clone() isn't implemented; see EJ page 61."""
for m in clazz.methods:
@@ -1854,6 +1922,7 @@
error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()")
+@verifier
def verify_pfd(clazz):
"""Verify that android APIs use PFD over FD."""
if clazz.fullname == "android.os.FileUtils" or clazz.fullname == "android.system.Os":
@@ -1875,6 +1944,7 @@
error(clazz, f, "FW11", "Must use ParcelFileDescriptor")
+@verifier
def verify_numbers(clazz):
"""Discourage small numbers types like short and byte."""
@@ -1896,8 +1966,10 @@
if arg in discouraged:
warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead")
+
PRIMITIVES = {"void", "int", "float", "boolean", "short", "char", "byte", "long", "double"}
+@verifier
def verify_nullability(clazz):
"""Catches missing nullability annotations"""
@@ -1926,6 +1998,8 @@
def has_nullability(annotations):
return "@NonNull" in annotations or "@Nullable" in annotations
+
+@verifier
def verify_singleton(clazz):
"""Catch singleton objects with constructors."""
@@ -1960,60 +2034,10 @@
if not is_interesting(clazz): return
- verify_constants(clazz)
- verify_enums(clazz)
- verify_class_names(clazz)
- verify_method_names(clazz)
- verify_callbacks(clazz)
- verify_listeners(clazz)
- verify_actions(clazz)
- verify_extras(clazz)
- verify_equals(clazz)
- verify_parcelable(clazz)
- verify_protected(clazz)
- verify_fields(clazz)
- verify_register(clazz)
- verify_sync(clazz)
- verify_intent_builder(clazz)
- verify_helper_classes(clazz)
- verify_builder(clazz)
- verify_aidl(clazz)
- verify_internal(clazz)
- verify_layering(clazz)
- verify_boolean(clazz)
- verify_collections(clazz)
- verify_flags(clazz)
- verify_exception(clazz)
+ for v in verifiers.itervalues():
+ v(clazz)
+
if not ALLOW_GOOGLE: verify_google(clazz)
- verify_bitset(clazz)
- verify_manager(clazz)
- verify_boxed(clazz)
- verify_static_utils(clazz)
- # verify_overload_args(clazz)
- verify_callback_handlers(clazz)
- verify_context_first(clazz)
- verify_listener_last(clazz)
- verify_resource_names(clazz)
- verify_files(clazz)
- verify_manager_list(clazz)
- verify_abstract_inner(clazz)
- verify_runtime_exceptions(clazz)
- verify_error(clazz)
- verify_units(clazz)
- verify_closable(clazz)
- verify_member_name_not_kotlin_keyword(clazz)
- verify_method_name_not_kotlin_operator(clazz)
- verify_collections_over_arrays(clazz)
- verify_user_handle(clazz)
- verify_params(clazz)
- verify_services(clazz)
- verify_tense(clazz)
- verify_icu(clazz)
- verify_clone(clazz)
- verify_pfd(clazz)
- verify_numbers(clazz)
- verify_singleton(clazz)
- verify_nullability(clazz)
def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None):
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
index ee97628..1ad7ada 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectableClassModel.java
@@ -308,7 +308,10 @@
* @see android.view.inspector.IntFlagMapping
* @see IntFlagEntry
*/
- INT_FLAG
+ INT_FLAG,
+
+ /** A resource ID */
+ RESOURCE_ID
}
}
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
index 6305228..20de90d 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectablePropertyProcessor.java
@@ -78,6 +78,54 @@
"androidx.annotation.ColorLong"};
/**
+ * Set of android and androidx annotation qualified names of resource ID annotations.
+ */
+ private static final String[] RESOURCE_ID_ANNOTATION_NAMES = {
+ "android.annotation.AnimatorRes",
+ "android.annotation.AnimRes",
+ "android.annotation.AnyRes",
+ "android.annotation.ArrayRes",
+ "android.annotation.BoolRes",
+ "android.annotation.DimenRes",
+ "android.annotation.DrawableRes",
+ "android.annotation.FontRes",
+ "android.annotation.IdRes",
+ "android.annotation.IntegerRes",
+ "android.annotation.InterpolatorRes",
+ "android.annotation.LayoutRes",
+ "android.annotation.MenuRes",
+ "android.annotation.NavigationRes",
+ "android.annotation.PluralsRes",
+ "android.annotation.RawRes",
+ "android.annotation.StringRes",
+ "android.annotation.StyleableRes",
+ "android.annotation.StyleRes",
+ "android.annotation.TransitionRes",
+ "android.annotation.XmlRes",
+ "androidx.annotation.AnimatorRes",
+ "androidx.annotation.AnimRes",
+ "androidx.annotation.AnyRes",
+ "androidx.annotation.ArrayRes",
+ "androidx.annotation.BoolRes",
+ "androidx.annotation.DimenRes",
+ "androidx.annotation.DrawableRes",
+ "androidx.annotation.FontRes",
+ "androidx.annotation.IdRes",
+ "androidx.annotation.IntegerRes",
+ "androidx.annotation.InterpolatorRes",
+ "androidx.annotation.LayoutRes",
+ "androidx.annotation.MenuRes",
+ "androidx.annotation.NavigationRes",
+ "androidx.annotation.PluralsRes",
+ "androidx.annotation.RawRes",
+ "androidx.annotation.StringRes",
+ "androidx.annotation.StyleableRes",
+ "androidx.annotation.StyleRes",
+ "androidx.annotation.TransitionRes",
+ "androidx.annotation.XmlRes"
+ };
+
+ /**
* @param annotationQualifiedName The qualified name of the annotation to process
* @param processingEnv The processing environment from the parent processor
*/
@@ -264,7 +312,6 @@
final Property.Type accessorType =
convertTypeMirrorToPropertyType(extractReturnOrFieldType(accessor), accessor);
- final boolean hasColor = hasColorAnnotation(accessor);
final Optional<AnnotationValue> enumMapping =
mAnnotationUtils.valueByName("enumMapping", annotation);
final Optional<AnnotationValue> flagMapping =
@@ -291,8 +338,12 @@
});
}
+
switch (valueType) {
case "INFERRED":
+ final boolean hasColor = hasColorAnnotation(accessor);
+ final boolean hasResourceId = hasResourceIdAnnotation(accessor);
+
if (hasColor) {
enumMapping.ifPresent(value -> {
throw new ProcessingException(
@@ -308,7 +359,30 @@
annotation,
value);
});
+ if (hasResourceId) {
+ throw new ProcessingException(
+ "Cannot infer type, both color and resource ID annotations "
+ + "are present.",
+ accessor,
+ annotation);
+ }
return Property.Type.COLOR;
+ } else if (hasResourceId) {
+ enumMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ "Cannot use enumMapping on a resource ID type.",
+ accessor,
+ annotation,
+ value);
+ });
+ flagMapping.ifPresent(value -> {
+ throw new ProcessingException(
+ "Cannot use flagMapping on a resource ID type.",
+ accessor,
+ annotation,
+ value);
+ });
+ return Property.Type.RESOURCE_ID;
} else if (enumMapping.isPresent()) {
flagMapping.ifPresent(value -> {
throw new ProcessingException(
@@ -346,6 +420,8 @@
case "INT_FLAG":
requirePackedIntToBeInt("IntFlag", accessorType, accessor, annotation);
return Property.Type.INT_FLAG;
+ case "RESOURCE_ID":
+ return Property.Type.RESOURCE_ID;
default:
throw new ProcessingException(
String.format("Unknown value type enumeration value: %s", valueType),
@@ -474,6 +550,24 @@
}
/**
+ * Determine if a getter or a field is annotated with a resource ID annotation.
+ *
+ * @param accessor The getter or field to query
+ * @return True if the accessor is an integer and has a resource ID annotation, false otherwise
+ */
+ private boolean hasResourceIdAnnotation(Element accessor) {
+ if (unboxType(extractReturnOrFieldType(accessor)) == TypeKind.INT) {
+ for (String name : RESOURCE_ID_ANNOTATION_NAMES) {
+ if (mAnnotationUtils.hasAnnotation(accessor, name)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Infer a property name from a getter method.
*
* If the method is prefixed with {@code get}, the prefix will be stripped, and the
diff --git a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
index 1ab9914..44d88bb 100644
--- a/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
+++ b/tools/processors/view_inspector/src/java/android/processor/view/inspector/InspectionCompanionGenerator.java
@@ -446,6 +446,8 @@
return "IntEnum";
case INT_FLAG:
return "IntFlag";
+ case RESOURCE_ID:
+ return "ResourceId";
default:
throw new NoSuchElementException(String.format("No such property type, %s", type));
}
diff --git a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
index 35f0fcf..4eed504 100644
--- a/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
+++ b/tools/processors/view_inspector/test/java/android/processor/view/inspector/InspectionCompanionGeneratorTest.java
@@ -82,6 +82,7 @@
addProperty("object", "getObject", Property.Type.OBJECT);
addProperty("color", "getColor", Property.Type.COLOR);
addProperty("gravity", "getGravity", Property.Type.GRAVITY);
+ addProperty("resourceId", "getResourceId", Property.Type.RESOURCE_ID);
assertGeneratedFileEquals("SimpleProperties");
}
diff --git a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
index dfc1bce..556d8dd 100644
--- a/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
+++ b/tools/processors/view_inspector/test/resources/android/processor/view/inspector/InspectionCompanionGeneratorTest/SimpleProperties.java.txt
@@ -69,6 +69,11 @@
private int mObjectId;
/**
+ * Property ID of {@code resourceId}.
+ */
+ private int mResourceIdId;
+
+ /**
* Property ID of {@code short}.
*/
private int mShortId;
@@ -85,6 +90,7 @@
mIntId = propertyMapper.mapInt("int", R.attr.int);
mLongId = propertyMapper.mapLong("long", R.attr.long);
mObjectId = propertyMapper.mapObject("object", R.attr.object);
+ mResourceIdId = propertyMapper.mapResourceId("resourceId", R.attr.resourceId);
mShortId = propertyMapper.mapShort("short", R.attr.short);
mPropertiesMapped = true;
}
@@ -104,6 +110,7 @@
propertyReader.readInt(mIntId, node.getInt());
propertyReader.readLong(mLongId, node.getLong());
propertyReader.readObject(mObjectId, node.getObject());
+ propertyReader.readResourceId(mResourceIdId, node.getResourceId());
propertyReader.readShort(mShortId, node.getShort());
}
}