1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.sdksandbox; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.Manifest; 22 import android.app.sdksandbox.testutils.ProtoUtil; 23 import android.content.Context; 24 import android.os.Build; 25 import android.provider.DeviceConfig; 26 import android.util.ArrayMap; 27 28 import androidx.test.platform.app.InstrumentationRegistry; 29 30 import com.android.server.sdksandbox.proto.Services; 31 32 import org.junit.After; 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.mockito.Mockito; 36 37 import java.util.List; 38 import java.util.Map; 39 40 public class SdkSandboxSettingsListenerUnitTest { 41 private static final String PROPERTY_DISABLE_SANDBOX = "disable_sdk_sandbox"; 42 private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = 43 "apply_sdk_sandbox_next_restrictions"; 44 private static final String PROPERTY_SERVICES_ALLOWLIST = 45 "services_allowlist_per_targetSdkVersion"; 46 private SdkSandboxSettingsListener mSdkSandboxSettingsListener; 47 48 @Before setUp()49 public void setUp() { 50 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 51 52 InstrumentationRegistry.getInstrumentation() 53 .getUiAutomation() 54 .adoptShellPermissionIdentity( 55 Manifest.permission.READ_DEVICE_CONFIG, 56 Manifest.permission.WRITE_DEVICE_CONFIG); 57 58 mSdkSandboxSettingsListener = 59 new SdkSandboxSettingsListener( 60 context, Mockito.mock(SdkSandboxManagerService.class)); 61 } 62 63 @After tearDown()64 public void tearDown() { 65 if (mSdkSandboxSettingsListener != null) { 66 mSdkSandboxSettingsListener.unregisterPropertiesListener(); 67 } 68 } 69 70 @Test testSdkSandboxSettings_killSwitch()71 public void testSdkSandboxSettings_killSwitch() { 72 assertThat(mSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse(); 73 setDeviceConfigProperty(PROPERTY_DISABLE_SANDBOX, "true"); 74 assertThat(mSdkSandboxSettingsListener.isKillSwitchEnabled()).isTrue(); 75 setDeviceConfigProperty(PROPERTY_DISABLE_SANDBOX, "false"); 76 assertThat(mSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse(); 77 } 78 79 @Test testOtherPropertyChangeDoesNotAffectKillSwitch()80 public void testOtherPropertyChangeDoesNotAffectKillSwitch() { 81 assertThat(mSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse(); 82 setDeviceConfigProperty("other_property", "true"); 83 assertThat(mSdkSandboxSettingsListener.isKillSwitchEnabled()).isFalse(); 84 } 85 86 @Test testSdkSandboxSettings_applySdkSandboxRestrictionsNext()87 public void testSdkSandboxSettings_applySdkSandboxRestrictionsNext() { 88 assertThat(mSdkSandboxSettingsListener.applySdkSandboxRestrictionsNext()).isFalse(); 89 setDeviceConfigProperty(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "true"); 90 assertThat(mSdkSandboxSettingsListener.applySdkSandboxRestrictionsNext()).isTrue(); 91 setDeviceConfigProperty(PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS, "false"); 92 assertThat(mSdkSandboxSettingsListener.applySdkSandboxRestrictionsNext()).isFalse(); 93 } 94 95 @Test testServiceAllowlist_DeviceConfigNotAvailable()96 public void testServiceAllowlist_DeviceConfigNotAvailable() { 97 setDeviceConfigProperty(PROPERTY_SERVICES_ALLOWLIST, null); 98 99 assertThat( 100 mSdkSandboxSettingsListener.getServiceAllowlistForTargetSdkVersion( 101 /*targetSdkVersion=*/ 34)) 102 .isNull(); 103 } 104 105 @Test testServiceAllowlist_DeviceConfigAllowlistApplied()106 public void testServiceAllowlist_DeviceConfigAllowlistApplied() { 107 /* 108 * Base64 encoded Service allowlist allowlist_per_target_sdk 109 * allowlist_per_target_sdk { 110 * key: 33 111 * value: { 112 * allowed_services: { 113 * action : "action.test.33" 114 * packageName : "packageName.test.33" 115 * componentClassName : "componentClassName.test.33" 116 * componentPackageName : "componentPackageName.test.33" 117 * } 118 * } 119 * } 120 * allowlist_per_target_sdk { 121 * key: 34 122 * value: { 123 * allowed_services: { 124 * action : "action.test.34" 125 * packageName : "packageName.test.34" 126 * componentClassName : "componentClassName.test.34" 127 * componentPackageName : "componentPackageName.test.34" 128 * } 129 * } 130 * } 131 */ 132 ArrayMap<Integer, List<ArrayMap<String, String>>> allowedServicesMap = new ArrayMap<>(); 133 134 allowedServicesMap.put( 135 Build.VERSION_CODES.TIRAMISU, 136 getAllowedServicesMap( 137 "action.test.33", "packageName.test.33", 138 "componentClassName.test.33", "componentPackageName.test.33")); 139 allowedServicesMap.put( 140 Build.VERSION_CODES.UPSIDE_DOWN_CAKE, 141 getAllowedServicesMap( 142 "action.test.34", "packageName.test.34", 143 "componentClassName.test.34", "componentPackageName.test.34")); 144 145 String encodedServiceAllowlist = ProtoUtil.encodeServiceAllowlist(allowedServicesMap); 146 147 setDeviceConfigProperty(PROPERTY_SERVICES_ALLOWLIST, encodedServiceAllowlist); 148 149 Services.AllowedServices allowedServices = 150 mSdkSandboxSettingsListener.getServiceAllowlistForTargetSdkVersion( 151 /*targetSdkVersion=*/ 33); 152 assertThat(allowedServices).isNotNull(); 153 154 verifyAllowlistEntryContents( 155 allowedServices.getAllowedServices(0), 156 /*action=*/ "action.test.33", 157 /*packageName=*/ "packageName.test.33", 158 /*componentClassName=*/ "componentClassName.test.33", 159 /*componentPackageName=*/ "componentPackageName.test.33"); 160 161 allowedServices = 162 mSdkSandboxSettingsListener.getServiceAllowlistForTargetSdkVersion( 163 /*targetSdkVersion=*/ 34); 164 assertThat(allowedServices).isNotNull(); 165 166 verifyAllowlistEntryContents( 167 allowedServices.getAllowedServices(0), 168 /*action=*/ "action.test.34", 169 /*packageName=*/ "packageName.test.34", 170 /*componentClassName=*/ "componentClassName.test.34", 171 /*componentPackageName=*/ "componentPackageName.test.34"); 172 } 173 verifyAllowlistEntryContents( Services.AllowedService allowedService, String action, String packageName, String componentClassName, String componentPackageName)174 private void verifyAllowlistEntryContents( 175 Services.AllowedService allowedService, 176 String action, 177 String packageName, 178 String componentClassName, 179 String componentPackageName) { 180 assertThat(allowedService.getAction()).isEqualTo(action); 181 assertThat(allowedService.getPackageName()).isEqualTo(packageName); 182 assertThat(allowedService.getComponentClassName()).isEqualTo(componentClassName); 183 assertThat(allowedService.getComponentPackageName()).isEqualTo(componentPackageName); 184 } 185 setDeviceConfigProperty(String property, String value)186 private void setDeviceConfigProperty(String property, String value) { 187 // Explicitly calling the onPropertiesChanged method to avoid race conditions 188 if (value == null) { 189 // Map.of() does not handle null, so we need to use an ArrayMap to delete a property 190 ArrayMap<String, String> properties = new ArrayMap<>(); 191 properties.put(property, null); 192 mSdkSandboxSettingsListener.onPropertiesChanged( 193 new DeviceConfig.Properties(DeviceConfig.NAMESPACE_ADSERVICES, properties)); 194 } else { 195 mSdkSandboxSettingsListener.onPropertiesChanged( 196 new DeviceConfig.Properties( 197 DeviceConfig.NAMESPACE_ADSERVICES, Map.of(property, value))); 198 } 199 } 200 getAllowedServicesMap( String action, String packageName, String componentClassName, String componentPackageName)201 private List<ArrayMap<String, String>> getAllowedServicesMap( 202 String action, 203 String packageName, 204 String componentClassName, 205 String componentPackageName) { 206 ArrayMap<String, String> data = new ArrayMap<>(/* capacity= */ 4); 207 data.put("action", action); 208 data.put("packageName", packageName); 209 data.put("componentClassName", componentClassName); 210 data.put("componentPackageName", componentPackageName); 211 212 return List.of(data); 213 } 214 } 215