Ban ConcurrentHashMap Usage
Bug: 129484383
Test: Manually confirmed that it detects fully qualified
references and imports.
Change-Id: I7c9da3044f55073b76da61f2c679379404a2a862
diff --git a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
index 5391fd4..eeec1f3 100644
--- a/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
+++ b/appcompat/appcompat/src/main/java/androidx/appcompat/widget/AppCompatTextViewAutoSizeHelper.java
@@ -18,6 +18,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -50,7 +51,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Utility class which encapsulates the logic for the TextView auto-size text feature added to
@@ -70,12 +70,14 @@
private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
// Cache of TextView methods used via reflection; the key is the method name and the value is
// the method itself or null if it can not be found.
- private static ConcurrentHashMap<String, Method> sTextViewMethodByNameCache =
- new ConcurrentHashMap<>();
+ @SuppressLint("BanConcurrentHashMap")
+ private static java.util.concurrent.ConcurrentHashMap<String, Method>
+ sTextViewMethodByNameCache = new java.util.concurrent.ConcurrentHashMap<>();
// Cache of TextView fields used via reflection; the key is the field name and the value is
// the field itself or null if it can not be found.
- private static ConcurrentHashMap<String, Field> sTextViewFieldByNameCache =
- new ConcurrentHashMap<>();
+ @SuppressLint("BanConcurrentHashMap")
+ private static java.util.concurrent.ConcurrentHashMap<String, Field> sTextViewFieldByNameCache =
+ new java.util.concurrent.ConcurrentHashMap<>();
// Use this to specify that any of the auto-size configuration int values have not been set.
static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1f;
// Ported from TextView#VERY_WIDE. Represents a maximum width in pixels the TextView takes when
diff --git a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
index 76b9345..ff9e7ce 100644
--- a/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
+++ b/core/core/src/main/java/androidx/core/graphics/TypefaceCompatBaseImpl.java
@@ -18,6 +18,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
@@ -35,7 +36,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Implementation of the Typeface compat methods for API 14 and above.
@@ -49,8 +49,9 @@
/**
* Maps a unique identifier from a Typeface to it's family
*/
- private ConcurrentHashMap<Long, FontFamilyFilesResourceEntry> mFontFamilies =
- new ConcurrentHashMap<>();
+ @SuppressLint("BanConcurrentHashMap")
+ private java.util.concurrent.ConcurrentHashMap<Long, FontFamilyFilesResourceEntry>
+ mFontFamilies = new java.util.concurrent.ConcurrentHashMap<>();
private interface StyleExtractor<T> {
int getWeight(T t);
diff --git a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
index 4ad1f69..420d922 100644
--- a/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
+++ b/lint-checks/src/main/java/androidx/build/lint/AndroidXIssueRegistry.kt
@@ -31,6 +31,7 @@
val Issues get(): List<Issue> {
return listOf(
BanParcelableUsage.ISSUE,
+ BanConcurrentHashMap.ISSUE,
BanKeepAnnotation.ISSUE,
BanTargetApiAnnotation.ISSUE,
MissingTestSizeAnnotation.ISSUE,
@@ -42,8 +43,8 @@
SampledAnnotationEnforcer.INVALID_SAMPLES_LOCATION,
ObsoleteBuildCompatUsageDetector.ISSUE,
BanSynchronizedMethods.ISSUE,
- MetadataTagInsideApplicationTagDetector.ISSUE,
- PrivateConstructorForUtilityClass.ISSUE
+ PrivateConstructorForUtilityClass.ISSUE,
+ MetadataTagInsideApplicationTagDetector.ISSUE
)
}
}
diff --git a/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt b/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt
new file mode 100644
index 0000000..1349313
--- /dev/null
+++ b/lint-checks/src/main/java/androidx/build/lint/BanConcurrentHashMap.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2020 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 androidx.build.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiField
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UImportStatement
+import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.USimpleNameReferenceExpression
+
+class BanConcurrentHashMap : Detector(), Detector.UastScanner {
+
+ override fun getApplicableUastTypes(): List<Class<out UElement>>? = listOf(
+ UImportStatement::class.java,
+ UQualifiedReferenceExpression::class.java
+ )
+
+ override fun createUastHandler(context: JavaContext): UElementHandler = object :
+ UElementHandler() {
+
+ // Detect fully qualified reference if not imported.
+ override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression) {
+ if (node.selector is USimpleNameReferenceExpression) {
+ val name = node.selector as USimpleNameReferenceExpression
+ if (CONCURRENT_HASHMAP.equals(name.identifier)) {
+ context.report(ISSUE, node, context.getLocation(node), "Detected " +
+ "ConcurrentHashMap usage.")
+ }
+ }
+ }
+
+ // Detect import.
+ override fun visitImportStatement(node: UImportStatement) {
+ if (node.importReference != null) {
+ var resolved = node.resolve()
+ if (node.resolve() is PsiField) {
+ resolved = (resolved as PsiField).containingClass
+ } else if (resolved is PsiMethod) {
+ resolved = resolved.containingClass
+ }
+ if (resolved is PsiClass && CONCURRENT_HASHMAP_QUALIFIED_NAME
+ .equals(resolved.qualifiedName)) {
+ context.report(ISSUE, node, context.getLocation(node), "Detected " +
+ "ConcurrentHashMap usage.")
+ }
+ }
+ }
+ }
+
+ companion object {
+ val ISSUE = Issue.create("BanConcurrentHashMap",
+ "ConcurrentHashMap usage is not allowed",
+ "ConcurrentHashMap has an issue on Android’s Lollipop release that can lead to lost" +
+ " updates under thread contention.",
+ Category.CORRECTNESS, 5, Severity.ERROR,
+ Implementation(BanConcurrentHashMap::class.java,
+ Scope.JAVA_FILE_SCOPE)
+ )
+ val CONCURRENT_HASHMAP_QUALIFIED_NAME = "java.util.concurrent.ConcurrentHashMap"
+ val CONCURRENT_HASHMAP = "ConcurrentHashMap"
+ }
+}
diff --git a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index eef111a..f3b8594 100644
--- a/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -19,6 +19,7 @@
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
@@ -63,7 +64,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
/**
* Allows an app to interact with an ongoing media session. Media buttons and
@@ -222,8 +222,9 @@
// This set is used to keep references to registered callbacks to prevent them being GCed,
// since we only keep weak references for callbacks in this class and its inner classes.
// It is actually a map not a set. Ignore the values and treat the keys as a set.
- private final ConcurrentHashMap<Callback, Boolean> mRegisteredCallbacks =
- new ConcurrentHashMap<>();
+ @SuppressLint("BanConcurrentHashMap")
+ private final java.util.concurrent.ConcurrentHashMap<Callback, Boolean> mRegisteredCallbacks =
+ new java.util.concurrent.ConcurrentHashMap<>();
/**
* Creates a media controller from a session.
diff --git a/room/runtime/src/main/java/androidx/room/RoomDatabase.java b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
index 6400f85..97f40e6 100644
--- a/room/runtime/src/main/java/androidx/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/androidx/room/RoomDatabase.java
@@ -128,7 +128,6 @@
return mSuspendingTransactionId;
}
-
private final Map<String, Object> mBackingFieldMap =
Collections.synchronizedMap(new HashMap<>());