diff options
| author | 2023-10-26 05:35:40 +0000 | |
|---|---|---|
| committer | 2023-11-22 22:32:17 +0000 | |
| commit | 086208e76d92d36747a95f0773d6ecbefff0c037 (patch) | |
| tree | 77da4430d6c45b92cd48f6e950fe01eb74ce0419 | |
| parent | e4387bfe1e58286e8f535836152352a74403f271 (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.txt | 8 | ||||
| -rw-r--r-- | core/java/android/app/SystemServiceRegistry.java | 13 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 12 | ||||
| -rw-r--r-- | core/java/android/os/ISecurityStateManager.aidl | 26 | ||||
| -rw-r--r-- | core/java/android/os/SecurityStateManager.java | 81 | ||||
| -rw-r--r-- | core/java/android/os/flags.aconfig | 7 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 5 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/SecurityStateManagerService.java | 109 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 9 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/SecurityStateTest.java | 118 |
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); + } +} |