Address all lint issues in androidx.core.os classes, including VFY

Relnote: Updated nullability for androidx.core.os classes
Fixes: 206113622
Test: MessageCompatTest, LocaleListCompatTest
Merged-In: If18cd2bcd8b5de33a5e47997339d32e9a13bc312
Change-Id: If18cd2bcd8b5de33a5e47997339d32e9a13bc312
diff --git a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/IoSettingsActivity.kt b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/IoSettingsActivity.kt
index b652055..17e5427 100644
--- a/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/IoSettingsActivity.kt
+++ b/compose/integration-tests/macrobenchmark-target/src/main/java/androidx/compose/integration/macrobenchmark/target/IoSettingsActivity.kt
@@ -150,7 +150,7 @@
 ) {
     Text(
         text = stringResource(R.string.io_about_title).uppercase(
-            LocaleListCompat.getDefault().get(0)
+            LocaleListCompat.getDefault().get(0)!!
         ),
         style = MaterialTheme.typography.body2,
         fontWeight = FontWeight.SemiBold,
diff --git a/core/core/api/current.txt b/core/core/api/current.txt
index 4bed9ff..a8121dd 100644
--- a/core/core/api/current.txt
+++ b/core/core/api/current.txt
@@ -1725,12 +1725,12 @@
   public final class LocaleListCompat {
     method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
     method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
-    method public java.util.Locale! get(int);
+    method public java.util.Locale? get(int);
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
     method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
     method public java.util.Locale? getFirstMatch(String![]);
-    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
     method public boolean isEmpty();
     method @IntRange(from=0) public int size();
     method public String toLanguageTags();
diff --git a/core/core/api/public_plus_experimental_current.txt b/core/core/api/public_plus_experimental_current.txt
index 8554f3b..044adaf 100644
--- a/core/core/api/public_plus_experimental_current.txt
+++ b/core/core/api/public_plus_experimental_current.txt
@@ -1730,12 +1730,12 @@
   public final class LocaleListCompat {
     method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
     method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
-    method public java.util.Locale! get(int);
+    method public java.util.Locale? get(int);
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
     method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
     method public java.util.Locale? getFirstMatch(String![]);
-    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
     method public boolean isEmpty();
     method @IntRange(from=0) public int size();
     method public String toLanguageTags();
diff --git a/core/core/api/restricted_current.txt b/core/core/api/restricted_current.txt
index d008d07..99839e8 100644
--- a/core/core/api/restricted_current.txt
+++ b/core/core/api/restricted_current.txt
@@ -2059,12 +2059,12 @@
   public final class LocaleListCompat {
     method public static androidx.core.os.LocaleListCompat create(java.util.Locale!...);
     method public static androidx.core.os.LocaleListCompat forLanguageTags(String?);
-    method public java.util.Locale! get(int);
+    method public java.util.Locale? get(int);
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getAdjustedDefault();
     method @Size(min=1) public static androidx.core.os.LocaleListCompat getDefault();
     method public static androidx.core.os.LocaleListCompat getEmptyLocaleList();
     method public java.util.Locale? getFirstMatch(String![]);
-    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale!);
+    method @IntRange(from=0xffffffff) public int indexOf(java.util.Locale?);
     method public boolean isEmpty();
     method @IntRange(from=0) public int size();
     method public String toLanguageTags();
diff --git a/core/core/src/main/java/androidx/core/os/CancellationSignal.java b/core/core/src/main/java/androidx/core/os/CancellationSignal.java
index e1b1fed..80e7c1b0 100644
--- a/core/core/src/main/java/androidx/core/os/CancellationSignal.java
+++ b/core/core/src/main/java/androidx/core/os/CancellationSignal.java
@@ -18,7 +18,9 @@
 
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 
 /**
  * Static library support version of the framework's {@link android.os.CancellationSignal}.
@@ -81,7 +83,7 @@
                 listener.onCancel();
             }
             if (obj != null && Build.VERSION.SDK_INT >= 16) {
-                ((android.os.CancellationSignal) obj).cancel();
+                Api16Impl.cancel(obj);
             }
         } finally {
             synchronized (this) {
@@ -126,7 +128,7 @@
      * Gets the framework {@link android.os.CancellationSignal} associated with this object.
      * <p>
      * Framework support for cancellation signals was added in
-     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} so this method will always
+     * {@link Build.VERSION_CODES#JELLY_BEAN} so this method will always
      * return null on older versions of the platform.
      * </p>
      *
@@ -140,9 +142,9 @@
         }
         synchronized (this) {
             if (mCancellationSignalObj == null) {
-                mCancellationSignalObj = new android.os.CancellationSignal();
+                mCancellationSignalObj = Api16Impl.createCancellationSignal();
                 if (mIsCanceled) {
-                    ((android.os.CancellationSignal) mCancellationSignalObj).cancel();
+                    Api16Impl.cancel(mCancellationSignalObj);
                 }
             }
             return mCancellationSignalObj;
@@ -154,6 +156,7 @@
             try {
                 wait();
             } catch (InterruptedException ex) {
+                // Do nothing
             }
         }
     }
@@ -167,4 +170,21 @@
          */
         void onCancel();
     }
+
+    @RequiresApi(16)
+    static class Api16Impl {
+        private Api16Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static void cancel(Object cancellationSignal) {
+            ((android.os.CancellationSignal) cancellationSignal).cancel();
+        }
+
+        @DoNotInline
+        static android.os.CancellationSignal createCancellationSignal() {
+            return new android.os.CancellationSignal();
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java b/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
index e6b4617..ba35171 100644
--- a/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
+++ b/core/core/src/main/java/androidx/core/os/ConfigurationCompat.java
@@ -20,7 +20,9 @@
 
 import android.content.res.Configuration;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 /**
  * Helper class which allows access to properties of {@link Configuration} in
@@ -39,9 +41,21 @@
     @NonNull
     public static LocaleListCompat getLocales(@NonNull Configuration configuration) {
         if (SDK_INT >= 24) {
-            return LocaleListCompat.wrap(configuration.getLocales());
+            return LocaleListCompat.wrap(Api24Impl.getLocales(configuration));
         } else {
             return LocaleListCompat.create(configuration.locale);
         }
     }
+
+    @RequiresApi(24)
+    static class Api24Impl {
+        private Api24Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static android.os.LocaleList getLocales(Configuration configuration) {
+            return configuration.getLocales();
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java b/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
index da61db8..78d1f2a 100644
--- a/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
+++ b/core/core/src/main/java/androidx/core/os/EnvironmentCompat.java
@@ -20,7 +20,9 @@
 import android.os.Environment;
 import android.util.Log;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 import java.io.File;
 import java.io.IOException;
@@ -57,9 +59,9 @@
     @NonNull
     public static String getStorageState(@NonNull File path) {
         if (Build.VERSION.SDK_INT >= 21) {
-            return Environment.getExternalStorageState(path);
+            return Api21Impl.getExternalStorageState(path);
         } else if (Build.VERSION.SDK_INT >= 19) {
-            return Environment.getStorageState(path);
+            return Api19Impl.getStorageState(path);
         }
 
         try {
@@ -78,5 +80,30 @@
         return MEDIA_UNKNOWN;
     }
 
-    private EnvironmentCompat() {}
+    private EnvironmentCompat() {
+    }
+
+    @RequiresApi(21)
+    static class Api21Impl {
+        private Api21Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static String getExternalStorageState(File path) {
+            return Environment.getExternalStorageState(path);
+        }
+    }
+
+    @RequiresApi(19)
+    static class Api19Impl {
+        private Api19Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static String getStorageState(File path) {
+            return Environment.getStorageState(path);
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/LocaleListCompat.java b/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
index 676b714..a09c8bf 100644
--- a/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
+++ b/core/core/src/main/java/androidx/core/os/LocaleListCompat.java
@@ -19,6 +19,7 @@
 import android.os.Build;
 import android.os.LocaleList;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -33,7 +34,7 @@
 public final class LocaleListCompat {
     private static final LocaleListCompat sEmptyLocaleList = create();
 
-    private LocaleListInterface mImpl;
+    private final LocaleListInterface mImpl;
 
     private LocaleListCompat(LocaleListInterface impl) {
         mImpl = impl;
@@ -71,7 +72,7 @@
     @NonNull
     public static LocaleListCompat create(@NonNull Locale... localeList) {
         if (Build.VERSION.SDK_INT >= 24) {
-            return wrap(new LocaleList(localeList));
+            return wrap(Api24Impl.createLocaleList(localeList));
         }
         return new LocaleListCompat(new LocaleListCompatWrapper(localeList));
     }
@@ -82,6 +83,7 @@
      * @param index The position to retrieve.
      * @return The {@link Locale} in the given index
      */
+    @Nullable
     public Locale get(int index) {
         return mImpl.get(index);
     }
@@ -113,7 +115,7 @@
      *         wasn't found
      */
     @IntRange(from = -1)
-    public int indexOf(Locale locale) {
+    public int indexOf(@Nullable Locale locale) {
         return mImpl.indexOf(locale);
     }
 
@@ -162,7 +164,7 @@
             final Locale[] localeArray = new Locale[tags.length];
             for (int i = 0; i < localeArray.length; i++) {
                 localeArray[i] = Build.VERSION.SDK_INT >= 21
-                        ? Locale.forLanguageTag(tags[i])
+                        ? Api21Impl.forLanguageTag(tags[i])
                         : forLanguageTagCompat(tags[i]);
             }
             return create(localeArray);
@@ -203,7 +205,7 @@
     @NonNull @Size(min = 1)
     public static LocaleListCompat getAdjustedDefault() {
         if (Build.VERSION.SDK_INT >= 24) {
-            return LocaleListCompat.wrap(LocaleList.getAdjustedDefault());
+            return LocaleListCompat.wrap(Api24Impl.getAdjustedDefault());
         } else {
             return LocaleListCompat.create(Locale.getDefault());
         }
@@ -223,7 +225,7 @@
     @NonNull @Size(min = 1)
     public static LocaleListCompat getDefault() {
         if (Build.VERSION.SDK_INT >= 24) {
-            return LocaleListCompat.wrap(LocaleList.getDefault());
+            return LocaleListCompat.wrap(Api24Impl.getDefault());
         } else {
             return LocaleListCompat.create(Locale.getDefault());
         }
@@ -239,8 +241,43 @@
         return mImpl.hashCode();
     }
 
+    @NonNull
     @Override
     public String toString() {
         return mImpl.toString();
     }
+
+    @RequiresApi(24)
+    static class Api24Impl {
+        private Api24Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static LocaleList createLocaleList(Locale... list) {
+            return new LocaleList(list);
+        }
+
+        @DoNotInline
+        static LocaleList getAdjustedDefault() {
+            return LocaleList.getAdjustedDefault();
+        }
+
+        @DoNotInline
+        static LocaleList getDefault() {
+            return LocaleList.getDefault();
+        }
+    }
+
+    @RequiresApi(21)
+    static class Api21Impl {
+        private Api21Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static Locale forLanguageTag(String languageTag) {
+            return Locale.forLanguageTag(languageTag);
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java b/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
index db9306a..94ebde2 100644
--- a/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
+++ b/core/core/src/main/java/androidx/core/os/LocaleListCompatWrapper.java
@@ -18,9 +18,11 @@
 
 import android.os.Build;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
@@ -94,12 +96,13 @@
     @Override
     public int hashCode() {
         int result = 1;
-        for (int i = 0; i < mList.length; i++) {
-            result = 31 * result + mList[i].hashCode();
+        for (Locale locale : mList) {
+            result = 31 * result + locale.hashCode();
         }
         return result;
     }
 
+    @NonNull
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -125,15 +128,13 @@
             mStringRepresentation = "";
         } else {
             final List<Locale> localeList = new ArrayList<>();
-            final HashSet<Locale> seenLocales = new HashSet<Locale>();
+            final HashSet<Locale> seenLocales = new HashSet<>();
             final StringBuilder sb = new StringBuilder();
             for (int i = 0; i < list.length; i++) {
                 final Locale l = list[i];
                 if (l == null) {
                     throw new NullPointerException("list[" + i + "] is null");
-                } else if (seenLocales.contains(l)) {
-                    // Ignore repeated locale
-                } else {
+                } else if (!seenLocales.contains(l)) {
                     final Locale localeClone = (Locale) l.clone();
                     localeList.add(localeClone);
                     toLanguageTag(sb, localeClone);
@@ -143,7 +144,7 @@
                     seenLocales.add(localeClone);
                 }
             }
-            mList = localeList.toArray(new Locale[localeList.size()]);
+            mList = localeList.toArray(new Locale[0]);
             mStringRepresentation = sb.toString();
         }
     }
@@ -152,6 +153,8 @@
     static void toLanguageTag(StringBuilder builder, Locale locale) {
         builder.append(locale.getLanguage());
         final String country = locale.getCountry();
+        // No guarantee that getCountry() was non-null in earlier SDKs.
+        //noinspection ConstantConditions
         if (country != null && !country.isEmpty()) {
             builder.append('-');
             builder.append(locale.getCountry());
@@ -160,7 +163,7 @@
 
     private static String getLikelyScript(Locale locale) {
         if (Build.VERSION.SDK_INT >= 21) {
-            final String script = locale.getScript();
+            final String script = Api21Impl.getScript(locale);
             if (!script.isEmpty()) {
                 return script;
             } else {
@@ -267,4 +270,16 @@
         return computeFirstMatch(Arrays.asList(supportedLocales),
                 false /* assume English is not supported */);
     }
+
+    @RequiresApi(21)
+    static class Api21Impl {
+        private Api21Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static String getScript(Locale locale) {
+            return locale.getScript();
+        }
+    }
 }
diff --git a/core/core/src/main/java/androidx/core/os/LocaleListPlatformWrapper.java b/core/core/src/main/java/androidx/core/os/LocaleListPlatformWrapper.java
index 3968e0f..0d96663 100644
--- a/core/core/src/main/java/androidx/core/os/LocaleListPlatformWrapper.java
+++ b/core/core/src/main/java/androidx/core/os/LocaleListPlatformWrapper.java
@@ -28,8 +28,8 @@
 final class LocaleListPlatformWrapper implements LocaleListInterface {
     private final LocaleList mLocaleList;
 
-    LocaleListPlatformWrapper(LocaleList localeList) {
-        mLocaleList = localeList;
+    LocaleListPlatformWrapper(Object localeList) {
+        mLocaleList = (LocaleList) localeList;
     }
 
     @Override
diff --git a/core/core/src/main/java/androidx/core/os/MessageCompat.java b/core/core/src/main/java/androidx/core/os/MessageCompat.java
index a278c0756..7340ab2 100644
--- a/core/core/src/main/java/androidx/core/os/MessageCompat.java
+++ b/core/core/src/main/java/androidx/core/os/MessageCompat.java
@@ -20,8 +20,11 @@
 import android.os.Build;
 import android.os.Looper;
 import android.os.Message;
+import android.view.View;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
 
 /**
  * Helper for accessing features in {@link Message}.
@@ -45,7 +48,7 @@
      * Certain operations, such as view invalidation, may introduce synchronization
      * barriers into the {@link Looper}'s message queue to prevent subsequent messages
      * from being delivered until some condition is met.  In the case of view invalidation,
-     * messages which are posted after a call to {@link android.view.View#invalidate}
+     * messages which are posted after a call to {@link View#invalidate}
      * are suspended by means of a synchronization barrier until the next frame is
      * ready to be drawn.  The synchronization barrier ensures that the invalidation
      * request is completely handled before resuming.
@@ -69,14 +72,14 @@
     @SuppressLint("NewApi")
     public static void setAsynchronous(@NonNull Message message, boolean async) {
         if (Build.VERSION.SDK_INT >= 22) {
-            message.setAsynchronous(async);
+            Api22Impl.setAsynchronous(message, async);
             return;
         }
         if (sTrySetAsynchronous && Build.VERSION.SDK_INT >= 16) {
             // Since this was an @hide method made public, we can link directly against it with a
             // try/catch for its absence instead of doing the same dance through reflection.
             try {
-                message.setAsynchronous(async);
+                Api22Impl.setAsynchronous(message, async);
             } catch (NoSuchMethodError e) {
                 sTrySetAsynchronous = false;
             }
@@ -95,13 +98,13 @@
     @SuppressLint("NewApi")
     public static boolean isAsynchronous(@NonNull Message message) {
         if (Build.VERSION.SDK_INT >= 22) {
-            return message.isAsynchronous();
+            return Api22Impl.isAsynchronous(message);
         }
         if (sTryIsAsynchronous && Build.VERSION.SDK_INT >= 16) {
             // Since this was an @hide method made public, we can link directly against it with a
             // try/catch for its absence instead of doing the same dance through reflection.
             try {
-                return message.isAsynchronous();
+                return Api22Impl.isAsynchronous(message);
             } catch (NoSuchMethodError e) {
                 sTryIsAsynchronous = false;
             }
@@ -111,4 +114,21 @@
 
     private MessageCompat() {
     }
+
+    @RequiresApi(22)
+    static class Api22Impl {
+        private Api22Impl() {
+            // This class is not instantiable.
+        }
+
+        @DoNotInline
+        static boolean isAsynchronous(Message message) {
+            return message.isAsynchronous();
+        }
+
+        @DoNotInline
+        static void setAsynchronous(Message message, boolean async) {
+            message.setAsynchronous(async);
+        }
+    }
 }