Handle hiddenapi lists which are yet unknown.

Make such APIs behave like @UnsupportedAppUsage.

Test: 822-hiddenapi-future
Bug: 172325244
Change-Id: Ic69e4d81fb64e0affb9c7c5e376bb19ab9e2d91f
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index c8b171d..375bf88 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -81,7 +81,7 @@
 class ApiList {
  private:
   // Number of bits reserved for Value in dex flags, and the corresponding bit mask.
-  static constexpr uint32_t kValueBitSize = 3;
+  static constexpr uint32_t kValueBitSize = 4;
   static constexpr uint32_t kValueBitMask = helper::BitMask(kValueBitSize);
 
   enum class Value : uint32_t {
@@ -141,6 +141,10 @@
     "max-target-r",
   };
 
+  // A magic marker used by tests to mimic a hiddenapi list which doesn't exist
+  // yet.
+  static constexpr const char* kFutureValueName = "max-target-future";
+
   // Names corresponding to DomainApis.
   static constexpr const char* kDomainApiNames[] {
     "core-platform-api",
@@ -172,9 +176,11 @@
     // Treat all ones as invalid value
     if (value == helper::ToUint(Value::kInvalid)) {
       return Value::kInvalid;
+    } else if (value > helper::ToUint(Value::kMax)) {
+      // For future unknown flag values, return unsupported.
+      return Value::kUnsupported;
     } else {
       DCHECK_GE(value, helper::ToUint(Value::kMin));
-      DCHECK_LE(value, helper::ToUint(Value::kMax));
       return static_cast<Value>(value);
     }
   }
@@ -216,6 +222,10 @@
         return ApiList(helper::GetEnumAt<DomainApi>(i));
       }
     }
+    if (str == kFutureValueName) {
+      static_assert(helper::ToUint(Value::kMax) + 1 < helper::ToUint(Value::kInvalid));
+      return ApiList(helper::ToUint(Value::kMax) + 1);
+    }
     return ApiList();
   }
 
diff --git a/test/822-hiddenapi-future/build b/test/822-hiddenapi-future/build
new file mode 100644
index 0000000..02ce549
--- /dev/null
+++ b/test/822-hiddenapi-future/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2021 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/822-hiddenapi-future/expected-stderr.txt b/test/822-hiddenapi-future/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/822-hiddenapi-future/expected-stderr.txt
diff --git a/test/822-hiddenapi-future/expected-stdout.txt b/test/822-hiddenapi-future/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/822-hiddenapi-future/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/822-hiddenapi-future/hiddenapi-flags.csv b/test/822-hiddenapi-future/hiddenapi-flags.csv
new file mode 100644
index 0000000..4f95ec0
--- /dev/null
+++ b/test/822-hiddenapi-future/hiddenapi-flags.csv
@@ -0,0 +1 @@
+LMyClass;->futureHidden()I,max-target-future
diff --git a/test/822-hiddenapi-future/info.txt b/test/822-hiddenapi-future/info.txt
new file mode 100644
index 0000000..6360d9b
--- /dev/null
+++ b/test/822-hiddenapi-future/info.txt
@@ -0,0 +1 @@
+Tests that future hiddenapi flags do not crash the runtime.
diff --git a/test/822-hiddenapi-future/src-ex/MyClass.java b/test/822-hiddenapi-future/src-ex/MyClass.java
new file mode 100644
index 0000000..4a9ec9c
--- /dev/null
+++ b/test/822-hiddenapi-future/src-ex/MyClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+public class MyClass {
+  public static int futureHidden() {
+    return 42;
+  }
+}
diff --git a/test/822-hiddenapi-future/src/Main.java b/test/822-hiddenapi-future/src/Main.java
new file mode 100644
index 0000000..f90a161
--- /dev/null
+++ b/test/822-hiddenapi-future/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+import java.io.File;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[0]);
+    init();
+    appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+
+    Class<?> klass = Object.class.getClassLoader().loadClass("MyClass");
+    Method m = klass.getDeclaredMethod("futureHidden");
+    Integer result = (Integer) m.invoke(null);
+    if (result.intValue() != 42) {
+      throw new Error("Expected 42, got " + result.intValue());
+    }
+  }
+
+  private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
+      "822-hiddenapi-future-ex.jar").getAbsolutePath();
+
+  private static native void init();
+  private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index d4b46ff..e3f4e88 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1140,6 +1140,7 @@
                   "817-hiddenapi",
                   "820-vdex-multidex",
                   "821-many-args",
+                  "822-hiddenapi-future",
                   "999-redefine-hiddenapi",
                   "1000-non-moving-space-stress",
                   "1001-app-image-regions",