summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Xu <alxu@google.com> 2023-10-26 05:35:40 +0000
committer Alex Xu <alxu@google.com> 2023-11-22 22:32:17 +0000
commit086208e76d92d36747a95f0773d6ecbefff0c037 (patch)
tree77da4430d6c45b92cd48f6e950fe01eb74ce0419
parente4387bfe1e58286e8f535836152352a74403f271 (diff)
Add security state API.
Adds API in android.os.SecurityStateManager. Currently the API provides a method to get the global security state. This returns system SPL, vendor SPL, mainline TVP SPLs, kernel version, WebView SPL, and SPLs for security state packages defined in core/res/res/values/config.xml. Design doc: go/dynamic-spl-api-design Bug: 300984416 Test: manual Change-Id: Ie1bcdb00adbd5495b2e4c812017c11b18fab7cf9
-rw-r--r--core/api/current.txt8
-rw-r--r--core/java/android/app/SystemServiceRegistry.java13
-rw-r--r--core/java/android/content/Context.java12
-rw-r--r--core/java/android/os/ISecurityStateManager.aidl26
-rw-r--r--core/java/android/os/SecurityStateManager.java81
-rw-r--r--core/java/android/os/flags.aconfig7
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/SecurityStateManagerService.java109
-rw-r--r--services/java/com/android/server/SystemServer.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/SecurityStateTest.java118
11 files changed, 389 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 812d4cd67ab7..98f668604168 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10598,6 +10598,7 @@ package android.content {
field public static final String RESTRICTIONS_SERVICE = "restrictions";
field public static final String ROLE_SERVICE = "role";
field public static final String SEARCH_SERVICE = "search";
+ field @FlaggedApi("android.os.security_state_service") public static final String SECURITY_STATE_SERVICE = "security_state";
field public static final String SENSOR_SERVICE = "sensor";
field public static final String SHORTCUT_SERVICE = "shortcut";
field public static final String STATUS_BAR_SERVICE = "statusbar";
@@ -33375,6 +33376,13 @@ package android.os {
field @NonNull public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ @FlaggedApi("android.os.security_state_service") public class SecurityStateManager {
+ method @FlaggedApi("android.os.security_state_service") @NonNull public android.os.Bundle getGlobalSecurityState();
+ field public static final String KEY_KERNEL_VERSION = "kernel_version";
+ field public static final String KEY_SYSTEM_SPL = "system_spl";
+ field public static final String KEY_VENDOR_SPL = "vendor_spl";
+ }
+
public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
method public void close();
method @NonNull public static android.os.SharedMemory create(@Nullable String, int) throws android.system.ErrnoException;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 79a5879b5cc0..cb941dff5fe3 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -174,6 +174,7 @@ import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IPowerStatsService;
import android.os.IRecoverySystem;
+import android.os.ISecurityStateManager;
import android.os.ISystemUpdateManager;
import android.os.IThermalService;
import android.os.IUserManager;
@@ -182,6 +183,7 @@ import android.os.PerformanceHintManager;
import android.os.PermissionEnforcer;
import android.os.PowerManager;
import android.os.RecoverySystem;
+import android.os.SecurityStateManager;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StatsFrameworkInitializer;
@@ -628,6 +630,17 @@ public final class SystemServiceRegistry {
ctx.mMainThread.getHandler());
}});
+ registerService(Context.SECURITY_STATE_SERVICE, SecurityStateManager.class,
+ new CachedServiceFetcher<SecurityStateManager>() {
+ @Override
+ public SecurityStateManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.SECURITY_STATE_SERVICE);
+ ISecurityStateManager service = ISecurityStateManager.Stub.asInterface(b);
+ return new SecurityStateManager(service);
+ }});
+
registerService(Context.SENSOR_SERVICE, SensorManager.class,
new CachedServiceFetcher<SensorManager>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1c6c7b5baa58..7c248f565123 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -72,6 +72,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.Flags;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -4214,6 +4215,7 @@ public abstract class Context {
DEVICE_LOCK_SERVICE,
VIRTUALIZATION_SERVICE,
GRAMMATICAL_INFLECTION_SERVICE,
+ SECURITY_STATE_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
@@ -6478,6 +6480,16 @@ public abstract class Context {
public static final String SHARED_CONNECTIVITY_SERVICE = "shared_connectivity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.os.SecurityStateManager} for accessing the security state manager service.
+ *
+ * @see #getSystemService(String)
+ * @see android.os.SecurityStateManager
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ public static final String SECURITY_STATE_SERVICE = "security_state";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/os/ISecurityStateManager.aidl b/core/java/android/os/ISecurityStateManager.aidl
new file mode 100644
index 000000000000..8ae624d0371d
--- /dev/null
+++ b/core/java/android/os/ISecurityStateManager.aidl
@@ -0,0 +1,26 @@
+/* //device/java/android/android/os/ISecurityStateManager.aidl
+**
+** Copyright 2023, 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 android.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISecurityStateManager {
+ Bundle getGlobalSecurityState();
+}
diff --git a/core/java/android/os/SecurityStateManager.java b/core/java/android/os/SecurityStateManager.java
new file mode 100644
index 000000000000..4fa61e0ca782
--- /dev/null
+++ b/core/java/android/os/SecurityStateManager.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 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 android.os;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * SecurityStateManager provides the functionality to query the security status of the system and
+ * platform components. For example, this includes the system and vendor security patch level.
+ */
+@FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+@SystemService(Context.SECURITY_STATE_SERVICE)
+public class SecurityStateManager {
+
+ /**
+ * The system SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_SYSTEM_SPL = "system_spl";
+
+ /**
+ * The vendor SPL key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_VENDOR_SPL = "vendor_spl";
+
+ /**
+ * The kernel version key returned as part of the {@code Bundle} from
+ * {@code getGlobalSecurityState}.
+ */
+ public static final String KEY_KERNEL_VERSION = "kernel_version";
+
+ private final ISecurityStateManager mService;
+
+ /**
+ * @hide
+ */
+ public SecurityStateManager(ISecurityStateManager service) {
+ mService = requireNonNull(service, "missing ISecurityStateManager");
+ }
+
+ /**
+ * Returns the current global security state. Each key-value pair is a mapping of a component
+ * of the global security state to its current version/SPL (security patch level). For example,
+ * the {@code KEY_SYSTEM_SPL} key will map to the SPL of the system as defined in
+ * {@link android.os.Build.VERSION}. The bundle will also include mappings from WebView packages
+ * and packages listed under config {@code config_securityStatePackages} to their respective
+ * versions as defined in {@link android.content.pm.PackageInfo#versionName}.
+ *
+ * @return A {@code Bundle} that contains the global security state information as
+ * string-to-string key-value pairs.
+ */
+ @FlaggedApi(Flags.FLAG_SECURITY_STATE_SERVICE)
+ @NonNull
+ public Bundle getGlobalSecurityState() {
+ try {
+ return mService.getGlobalSecurityState();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index a78f221fc962..c085f334457a 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -57,6 +57,13 @@ flag {
}
flag {
+ name: "security_state_service"
+ namespace: "dynamic_spl"
+ description: "Guards the Security State API."
+ bug: "302189431"
+}
+
+flag {
name: "battery_saver_supported_check_api"
namespace: "backstage_power"
description: "Guards a new API in PowerManager to check if battery saver is supported or not."
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6cd6eb4b8df9..bb3eaa42370a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4986,6 +4986,11 @@
<!-- Component name for the default module metadata provider on this device -->
<string name="config_defaultModuleMetadataProvider" translatable="false">com.android.modulemetadata</string>
+ <!-- Packages that contain a security state.
+ {@link SecurityStateManager#getGlobalSecurityState} will read and report the state/version
+ of these packages. -->
+ <string-array name="config_securityStatePackages" translatable="false" />
+
<!-- Package name for the default Health Connect app.
OEMs can set this with their own health app package name to define a default app with high
priority for the app to store the health data. If set the app always has priority of 1
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 24b39bc1225e..ac8d5c135efc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1267,6 +1267,7 @@
<java-symbol type="array" name="policy_exempt_apps" />
<java-symbol type="array" name="vendor_policy_exempt_apps" />
<java-symbol type="array" name="cloneable_apps" />
+ <java-symbol type="array" name="config_securityStatePackages" />
<java-symbol type="drawable" name="default_wallpaper" />
<java-symbol type="drawable" name="default_lock_wallpaper" />
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
new file mode 100644
index 000000000000..98039be20897
--- /dev/null
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2023 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 com.android.server;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ISecurityStateManager;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewUpdateService;
+
+import com.android.internal.R;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class SecurityStateManagerService extends ISecurityStateManager.Stub {
+
+ private static final String TAG = "SecurityStateManagerService";
+
+ static final String VENDOR_SECURITY_PATCH_PROPERTY_KEY = "ro.vendor.build"
+ + ".security_patch";
+ static final Pattern KERNEL_RELEASE_PATTERN = Pattern.compile("(\\d+\\.\\d+\\.\\d+)("
+ + ".*)");
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public SecurityStateManagerService(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public Bundle getGlobalSecurityState() {
+ Bundle globalSecurityState = new Bundle();
+ globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
+ globalSecurityState.putString(KEY_VENDOR_SPL,
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ String moduleMetadataProviderPackageName =
+ mContext.getString(R.string.config_defaultModuleMetadataProvider);
+ if (!moduleMetadataProviderPackageName.isEmpty()) {
+ globalSecurityState.putString(moduleMetadataProviderPackageName,
+ getSpl(moduleMetadataProviderPackageName));
+ }
+ globalSecurityState.putString(KEY_KERNEL_VERSION, getKernelVersion());
+ addWebViewPackages(globalSecurityState);
+ addSecurityStatePackages(globalSecurityState);
+ return globalSecurityState;
+ }
+
+ private String getSpl(String packageName) {
+ if (!TextUtils.isEmpty(packageName)) {
+ try {
+ return mPackageManager.getPackageInfo(packageName, 0 /* flags */).versionName;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, TextUtils.formatSimple("Failed to get SPL for package %s.",
+ packageName), e);
+ }
+ }
+ return "";
+ }
+
+ private String getKernelVersion() {
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return "";
+ }
+
+ private void addWebViewPackages(Bundle bundle) {
+ for (WebViewProviderInfo info : WebViewUpdateService.getAllWebViewPackages()) {
+ String packageName = info.packageName;
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+
+ private void addSecurityStatePackages(Bundle bundle) {
+ String[] packageNames;
+ packageNames = mContext.getResources().getStringArray(R.array.config_securityStatePackages);
+ for (String packageName : packageNames) {
+ bundle.putString(packageName, getSpl(packageName));
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0a2e80606e96..1919eb33c38c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2112,6 +2112,15 @@ public final class SystemServer implements Dumpable {
networkPolicy.bindConnectivityManager();
t.traceEnd();
+ t.traceBegin("StartSecurityStateManagerService");
+ try {
+ ServiceManager.addService(Context.SECURITY_STATE_SERVICE,
+ new SecurityStateManagerService(context));
+ } catch (Throwable e) {
+ reportWtf("starting SecurityStateManagerService", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartVpnManagerService");
try {
vpnManager = VpnManagerService.create(context);
diff --git a/services/tests/servicestests/src/com/android/server/SecurityStateTest.java b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
new file mode 100644
index 000000000000..fc91e47534f1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/SecurityStateTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2023 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 com.android.server;
+
+import static android.os.SecurityStateManager.KEY_KERNEL_VERSION;
+import static android.os.SecurityStateManager.KEY_SYSTEM_SPL;
+import static android.os.SecurityStateManager.KEY_VENDOR_SPL;
+
+import static com.android.server.SecurityStateManagerService.KERNEL_RELEASE_PATTERN;
+import static com.android.server.SecurityStateManagerService.VENDOR_SECURITY_PATCH_PROPERTY_KEY;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.regex.Matcher;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SecurityStateTest {
+ @Rule
+ public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+ @Mock
+ private Context mMockContext;
+
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Mock
+ private Resources mMockResources;
+
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER = "com.android.modulemetadata";
+ private static final String DEFAULT_MODULE_METADATA_PROVIDER_VERSION = "2023-12-01";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE = "com.google.android.gms";
+ private static final String DEFAULT_SECURITY_STATE_PACKAGE_VERSION = "2023-12-05";
+ private static final String[] SECURITY_STATE_PACKAGES =
+ new String[]{DEFAULT_SECURITY_STATE_PACKAGE};
+
+ @Before
+ public void setUp() throws Exception {
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockContext.getResources()).thenReturn(mMockResources);
+ when(mMockContext.getString(R.string.config_defaultModuleMetadataProvider))
+ .thenReturn(DEFAULT_MODULE_METADATA_PROVIDER);
+ when(mMockPackageManager.getPackageInfo(anyString(), anyInt()))
+ .thenReturn(new PackageInfo());
+ PackageInfo moduleMetadataPackageInfo = new PackageInfo();
+ moduleMetadataPackageInfo.versionName = DEFAULT_MODULE_METADATA_PROVIDER_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_MODULE_METADATA_PROVIDER, 0))
+ .thenReturn(moduleMetadataPackageInfo);
+ PackageInfo securityStatePackageInfo = new PackageInfo();
+ securityStatePackageInfo.versionName = DEFAULT_SECURITY_STATE_PACKAGE_VERSION;
+ when(mMockPackageManager.getPackageInfo(DEFAULT_SECURITY_STATE_PACKAGE, 0))
+ .thenReturn(securityStatePackageInfo);
+ when(mMockResources.getStringArray(R.array.config_securityStatePackages))
+ .thenReturn(SECURITY_STATE_PACKAGES);
+ }
+
+ @Test
+ public void testGetGlobalSecurityState_returnsBundle() {
+ SecurityStateManagerService securityState = new SecurityStateManagerService(mMockContext);
+
+ Bundle bundle = securityState.getGlobalSecurityState();
+
+ assertEquals(bundle.getString(KEY_SYSTEM_SPL), Build.VERSION.SECURITY_PATCH);
+ assertEquals(bundle.getString(KEY_VENDOR_SPL),
+ SystemProperties.get(VENDOR_SECURITY_PATCH_PROPERTY_KEY, ""));
+ Matcher matcher = KERNEL_RELEASE_PATTERN.matcher(VintfRuntimeInfo.getKernelVersion());
+ String kernelVersion = "";
+ if (matcher.matches()) {
+ kernelVersion = matcher.group(1);
+ }
+ assertEquals(bundle.getString(KEY_KERNEL_VERSION), kernelVersion);
+ assertEquals(bundle.getString(DEFAULT_MODULE_METADATA_PROVIDER),
+ DEFAULT_MODULE_METADATA_PROVIDER_VERSION);
+ assertEquals(bundle.getString(DEFAULT_SECURITY_STATE_PACKAGE),
+ DEFAULT_SECURITY_STATE_PACKAGE_VERSION);
+ }
+}