Adds call to android.os.SecurityStateManager.getGlobalSecurityState() for API >= 35

Test: Updated device tests.
Relnote: `getGlobalSecurityState()` now returns the global security state from the system service for SDK 35+.
Bug: 369392426
Change-Id: I7b9dac324d0b24748e586c26600914037c2960c5
diff --git a/security/security-state/build.gradle b/security/security-state/build.gradle
index 48b0c87..92c605c 100644
--- a/security/security-state/build.gradle
+++ b/security/security-state/build.gradle
@@ -54,6 +54,7 @@
 }
 
 android {
+    compileSdk 35
     namespace "androidx.security.state"
 }
 
diff --git a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
index d9ec8e5c..d0852df 100644
--- a/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
+++ b/security/security-state/src/androidTest/java/androidx/security/state/SecurityStateManagerTest.kt
@@ -18,10 +18,12 @@
 
 import android.content.Context
 import android.os.Build
+import android.os.Bundle
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import androidx.test.filters.SdkSuppress
+import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -29,7 +31,6 @@
 
 @MediumTest
 @RunWith(AndroidJUnit4::class)
-@SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
 class SecurityStateManagerTest {
 
     private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -40,37 +41,90 @@
         securityStateManager = SecurityStateManager(context)
     }
 
-    @Test
-    fun testGetGlobalSecurityState() {
-        val bundle = securityStateManager.getGlobalSecurityState()
-
-        // Check if dates are in the format YYYY-MM-DD
+    /** Returns `true` if [date] is in the format "YYYY-MM-DD". */
+    private fun matchesDateFormat(date: String): Boolean {
         val dateRegex = "^\\d{4}-\\d{2}-\\d{2}$"
-        assertTrue(bundle.getString("system_spl")!!.matches(dateRegex.toRegex()))
-        assertTrue(bundle.getString("vendor_spl")!!.matches(dateRegex.toRegex()))
+        return date.matches(dateRegex.toRegex())
+    }
 
-        // Check if kernel version is in the format X.X.XX
+    /** Returns `true` if [kernel] is in the format "X.X.XX". */
+    private fun matchesKernelFormat(kernel: String): Boolean {
         val versionRegex = "^\\d+\\.\\d+\\.\\d+$"
-        assertTrue(bundle.getString("kernel_version")!!.matches(versionRegex.toRegex()))
+        return kernel.matches(versionRegex.toRegex())
+    }
 
-        // Webview keys are expected to have specific naming and version formats
+    /** Returns `true` if a key for a WebView package exists in [bundle]. */
+    private fun containsWebViewPackage(bundle: Bundle): Boolean {
         var foundWebView = false
-        val nameRegex = "^com\\.[a-zA-Z0-9_.]+\\.webview$"
+        // https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/webview-providers.md#package-name
+        val nameRegex =
+            "^(?:com\\.google\\.android\\.apps\\.chrome|com\\.google\\.android\\.webview|com\\.android\\.(?:chrome|webview)|com\\.chrome)(?:\\.(?:beta|dev|canary|debug))?\$"
         val versionRegexWebView = "^\\d+\\.\\d+\\.\\d+\\.\\d+$"
         for (key in bundle.keySet()) {
-            if (key.contains("webview")) {
+            if (key.matches(nameRegex.toRegex())) {
                 foundWebView = true
-                val nameMatch = key.matches(nameRegex.toRegex())
-                val versionMatch = bundle.getString(key)!!.matches(versionRegexWebView.toRegex())
-
-                assertTrue("Webview name format incorrect: $key", nameMatch)
-                assertTrue(
-                    "Webview version format incorrect for $key: ${bundle.getString(key)}",
-                    versionMatch
-                )
-                break
+                val value = bundle.getString(key)
+                if (value!!.isNotEmpty()) {
+                    assertTrue(
+                        "WebView version format incorrect for $key: $value",
+                        value.matches(versionRegexWebView.toRegex())
+                    )
+                    break
+                }
             }
         }
-        assertTrue("No webview key found in bundle", foundWebView)
+        return foundWebView
+    }
+
+    /** Returns `true` if a key for the module metadata package name exists in [bundle]. */
+    private fun containsModuleMetadataPackage(bundle: Bundle): Boolean {
+        return bundle.keySet().any { it.contains("modulemetadata") }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
+    @Test
+    fun testGetGlobalSecurityState_sdkAbove29() {
+        val bundle = securityStateManager.getGlobalSecurityState()
+        assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
+        assertTrue(matchesDateFormat(bundle.getString("vendor_spl")!!))
+        assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
+        assertTrue(containsModuleMetadataPackage(bundle))
+        assertTrue(containsWebViewPackage(bundle))
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O, maxSdkVersion = Build.VERSION_CODES.P)
+    @Test
+    fun testGetGlobalSecurityState_sdkAbove25Below29_doesNotContainModuleMetadata() {
+        val bundle = securityStateManager.getGlobalSecurityState()
+        assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
+        assertTrue(matchesDateFormat(bundle.getString("vendor_spl")!!))
+        assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
+        assertTrue(containsWebViewPackage(bundle))
+        assertFalse(containsModuleMetadataPackage(bundle))
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.M, maxSdkVersion = Build.VERSION_CODES.N_MR1)
+    @Test
+    fun testGetGlobalSecurityState_sdkAbove22Below26_doesNotContainModuleMetadataOrWebView() {
+        val bundle = securityStateManager.getGlobalSecurityState()
+        assertTrue(matchesDateFormat(bundle.getString("system_spl")!!))
+        assertTrue(matchesDateFormat(bundle.getString("vendor_spl")!!))
+        assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
+        assertFalse(containsModuleMetadataPackage(bundle))
+        assertFalse(containsWebViewPackage(bundle))
+    }
+
+    @SdkSuppress(
+        minSdkVersion = Build.VERSION_CODES.LOLLIPOP,
+        maxSdkVersion = Build.VERSION_CODES.LOLLIPOP_MR1
+    )
+    @Test
+    fun testGetGlobalSecurityState_sdkBelow23_containsOnlyKernel() {
+        val bundle = securityStateManager.getGlobalSecurityState()
+        assertTrue(matchesKernelFormat(bundle.getString("kernel_version")!!))
+        assertFalse(bundle.containsKey("system_spl"))
+        assertFalse(bundle.containsKey("vendor_spl"))
+        assertFalse(containsModuleMetadataPackage(bundle))
+        assertFalse(containsWebViewPackage(bundle))
     }
 }
diff --git a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt b/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
index 011d418..9329c9b 100644
--- a/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
+++ b/security/security-state/src/main/java/androidx/security/state/SecurityStateManager.kt
@@ -27,7 +27,7 @@
 import java.util.regex.Pattern
 
 /**
- * This class is a wrapper around AOSP {@link android.os.SecurityStateManager} service API added in
+ * This class is a wrapper around AOSP [android.os.SecurityStateManager] service API added in
  * SDK 35. Support for features on older SDKs is provided on a best effort basis.
  *
  * Manages the retrieval and storage of security patch levels and module information for an Android
@@ -81,8 +81,10 @@
      */
     @SuppressLint("NewApi") // Lint does not detect version check below.
     public open fun getGlobalSecurityState(moduleMetadataProvider: String? = null): Bundle {
+        if (getAndroidSdkInt() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            return getGlobalSecurityStateFromService()
+        }
         return Bundle().apply {
-            // TODO(musashi): add call to SecurityStateManager API when it becomes available
             if (getAndroidSdkInt() >= Build.VERSION_CODES.M) {
                 putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH)
 
@@ -114,6 +116,30 @@
     }
 
     /**
+     * Returns the current global security state from the system service on SDK 35+.
+     *
+     * @return A [Bundle] that contains the global security state information as string-to-string
+     *   key-value pairs.
+     */
+    @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+    @SuppressLint("WrongConstant")
+    private fun getGlobalSecurityStateFromService(): Bundle {
+        val securityStateManagerService =
+            context.getSystemService(Context.SECURITY_STATE_SERVICE)
+                as android.os.SecurityStateManager
+        val globalSecurityState = securityStateManagerService.globalSecurityState
+        val vendorSpl = globalSecurityState.getString(KEY_VENDOR_SPL, "")
+        if (vendorSpl.isEmpty()) {
+            // Assume vendor SPL == system SPL
+            globalSecurityState.putString(
+                KEY_VENDOR_SPL,
+                globalSecurityState.getString(KEY_SYSTEM_SPL)
+            )
+        }
+        return globalSecurityState
+    }
+
+    /**
      * Fetches the security patch level (SPL) for a specific package by its package name. This is
      * typically used to get version information for modules that may have their own update cycles
      * independent of the system OS.