[go: nahoru, domu]

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<>());