1 /* 2 * Copyright (C) 2019 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 android.ext.services.watchdog; 18 19 import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_REQUESTED_PACKAGES; 20 import static android.service.watchdog.ExplicitHealthCheckService.EXTRA_SUPPORTED_PACKAGES; 21 import static android.service.watchdog.ExplicitHealthCheckService.PackageConfig; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeFalse; 27 28 import android.Manifest; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.PackageManager; 33 import android.content.pm.ResolveInfo; 34 import android.content.pm.ServiceInfo; 35 import android.os.RemoteCallback; 36 import android.provider.DeviceConfig; 37 import android.service.watchdog.ExplicitHealthCheckService; 38 import android.service.watchdog.IExplicitHealthCheckService; 39 40 import androidx.test.InstrumentationRegistry; 41 import androidx.test.rule.ServiceTestRule; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Ignore; 46 import org.junit.Rule; 47 import org.junit.Test; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.concurrent.CountDownLatch; 52 53 /** 54 * Contains the base tests that does not rely on the specific algorithm implementation. 55 */ 56 public class ExplicitHealthCheckServiceImplTest { 57 private static final String NETWORK_STACK_CONNECTOR_CLASS = 58 "android.net.INetworkStackConnector"; 59 private static final String PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED = 60 "watchdog_explicit_health_check_enabled"; 61 62 private final Context mContext = InstrumentationRegistry.getContext(); 63 private IExplicitHealthCheckService mService; 64 private String mNetworkStackPackageName; 65 66 @Rule 67 public ServiceTestRule mServiceTestRule; 68 69 @Before setUp()70 public void setUp() throws Exception { 71 InstrumentationRegistry 72 .getInstrumentation() 73 .getUiAutomation() 74 .adoptShellPermissionIdentity( 75 Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE, 76 Manifest.permission.WRITE_DEVICE_CONFIG); 77 78 executeShellCommand("svc wifi disable"); 79 executeShellCommand("svc data disable"); 80 mServiceTestRule = new ServiceTestRule(); 81 mService = IExplicitHealthCheckService.Stub.asInterface( 82 mServiceTestRule.bindService(getExtServiceIntent())); 83 mNetworkStackPackageName = getNetworkStackPackage(); 84 assumeFalse(mNetworkStackPackageName == null); 85 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 86 ExplicitHealthCheckServiceImpl.PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS, 87 Long.toString(ExplicitHealthCheckServiceImpl.DEFAULT_REQUEST_TIMEOUT_MILLIS), 88 false); 89 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 90 PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 91 Boolean.toString(false), /* makeDefault */ false); 92 } 93 94 @After tearDown()95 public void tearDown() { 96 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK, 97 PROPERTY_WATCHDOG_EXPLICIT_HEALTH_CHECK_ENABLED, 98 Boolean.toString(true), /* makeDefault */ false); 99 executeShellCommand("svc wifi enable"); 100 executeShellCommand("svc data enable"); 101 InstrumentationRegistry 102 .getInstrumentation() 103 .getUiAutomation() 104 .dropShellPermissionIdentity(); 105 } 106 107 @Test testHealthCheckSupportedPackage()108 public void testHealthCheckSupportedPackage() throws Exception { 109 List<PackageConfig> supportedPackages = new ArrayList<>(); 110 CountDownLatch latch = new CountDownLatch(1); 111 112 mService.getSupportedPackages(new RemoteCallback(result -> { 113 supportedPackages.addAll(result.getParcelableArrayList(EXTRA_SUPPORTED_PACKAGES)); 114 latch.countDown(); 115 })); 116 latch.await(); 117 118 // TODO: Support DeviceConfig changes for the health check timeout 119 assertThat(supportedPackages).hasSize(1); 120 assertThat(supportedPackages.get(0).getPackageName()) 121 .isEqualTo(mNetworkStackPackageName); 122 assertThat(supportedPackages.get(0).getHealthCheckTimeoutMillis()) 123 .isEqualTo(ExplicitHealthCheckServiceImpl.DEFAULT_REQUEST_TIMEOUT_MILLIS); 124 } 125 126 @Test 127 @Ignore testHealthCheckRequests()128 public void testHealthCheckRequests() throws Exception { 129 List<String> requestedPackages = new ArrayList<>(); 130 CountDownLatch latch1 = new CountDownLatch(1); 131 CountDownLatch latch2 = new CountDownLatch(1); 132 CountDownLatch latch3 = new CountDownLatch(1); 133 134 // Initially, no health checks requested 135 mService.getRequestedPackages(new RemoteCallback(result -> { 136 requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES)); 137 latch1.countDown(); 138 })); 139 140 // Verify that no health checks requested 141 latch1.await(); 142 assertThat(requestedPackages).isEmpty(); 143 144 // Then request health check 145 mService.request(mNetworkStackPackageName); 146 147 // Verify that health check is requested for network stack 148 mService.getRequestedPackages(new RemoteCallback(result -> { 149 requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES)); 150 latch2.countDown(); 151 })); 152 latch2.await(); 153 assertThat(requestedPackages).hasSize(1); 154 assertThat(requestedPackages.get(0)).isEqualTo(mNetworkStackPackageName); 155 156 // Then cancel health check 157 requestedPackages.clear(); 158 mService.cancel(mNetworkStackPackageName); 159 160 // Verify that health check is cancelled for network stack 161 mService.getRequestedPackages(new RemoteCallback(result -> { 162 requestedPackages.addAll(result.getParcelableArrayList(EXTRA_REQUESTED_PACKAGES)); 163 latch3.countDown(); 164 })); 165 latch3.await(); 166 assertThat(requestedPackages).isEmpty(); 167 } 168 getNetworkStackPackage()169 private String getNetworkStackPackage() { 170 Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS); 171 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 172 if (comp != null) { 173 return comp.getPackageName(); 174 } else { 175 // On Go devices, or any device that does not ship the network stack module. 176 // The network stack will live in system_server process, so no need to monitor. 177 return null; 178 } 179 } 180 getExtServiceIntent()181 private Intent getExtServiceIntent() { 182 ComponentName component = getExtServiceComponentNameLocked(); 183 if (component == null) { 184 fail("Health check service not found"); 185 } 186 Intent intent = new Intent(); 187 intent.setComponent(component); 188 return intent; 189 } 190 getExtServiceComponentNameLocked()191 private ComponentName getExtServiceComponentNameLocked() { 192 ServiceInfo serviceInfo = getExtServiceInfoLocked(); 193 if (serviceInfo == null) { 194 return null; 195 } 196 197 final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name); 198 if (!Manifest.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE 199 .equals(serviceInfo.permission)) { 200 return null; 201 } 202 return name; 203 } 204 getExtServiceInfoLocked()205 private ServiceInfo getExtServiceInfoLocked() { 206 final String packageName = 207 mContext.getPackageManager().getServicesSystemSharedLibraryPackageName(); 208 if (packageName == null) { 209 return null; 210 } 211 212 final Intent intent = new Intent(ExplicitHealthCheckService.SERVICE_INTERFACE); 213 intent.setPackage(packageName); 214 final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent, 215 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); 216 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 217 return null; 218 } 219 return resolveInfo.serviceInfo; 220 } 221 executeShellCommand(String command)222 private void executeShellCommand(String command) { 223 InstrumentationRegistry 224 .getInstrumentation() 225 .getUiAutomation() 226 .executeShellCommand(command); 227 } 228 } 229