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.adservices.common; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import android.annotation.NonNull; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.content.pm.ServiceInfo; 27 import android.os.Build; 28 import android.os.Process; 29 import android.os.SystemProperties; 30 import android.util.Log; 31 32 import com.android.compatibility.common.util.ShellUtils; 33 import com.android.modules.utils.build.SdkLevel; 34 35 import java.util.List; 36 37 /** Class to place Adservices CTS related helper method. */ 38 public final class AdservicesTestHelper { 39 // Used to get the package name. Copied over from com.android.adservices.AdServicesCommon 40 private static final String MEASUREMENT_SERVICE_NAME = "android.adservices.MEASUREMENT_SERVICE"; 41 private static final String DEFAULT_LOG_TAG = "adservices"; 42 private static final String FORCE_KILL_PROCESS_COMMAND = "am force-stop"; 43 // Used to differentiate between AdServices APK package name and AdExtServices APK package name. 44 private static final String ADSERVICES_APK_PACKAGE_NAME_SUFFIX = "android.adservices.api"; 45 46 /** 47 * Used to get the package name. Copied over from com.android.adservices.AndroidServiceBinder 48 * 49 * @param context the context 50 * @param logTag the tag used for logging 51 * @return Adservices package name 52 * @deprecated use {@link AdServicesSupportHelper#getAdServicesPackageName()} instead. 53 */ 54 @Deprecated getAdServicesPackageName( @onNull Context context, @NonNull String logTag)55 public static String getAdServicesPackageName( 56 @NonNull Context context, @NonNull String logTag) { 57 final Intent intent = new Intent(MEASUREMENT_SERVICE_NAME); 58 final List<ResolveInfo> resolveInfos = 59 context.getPackageManager() 60 .queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY); 61 final ServiceInfo serviceInfo = 62 resolveAdServicesService(resolveInfos, MEASUREMENT_SERVICE_NAME, logTag); 63 if (serviceInfo == null) { 64 Log.e(logTag, "Failed to find serviceInfo for adServices service"); 65 return null; 66 } 67 68 return serviceInfo.packageName; 69 } 70 71 /** 72 * Used to get the package name. An overloading method of {@code 73 * getAdservicesPackageName(context, logTag)} by using {@code DEFAULT_LOG_TAG}. 74 * 75 * @param context the context 76 * @return Adservices package name 77 * @deprecated use {@link AdServicesSupportHelper#getAdServicesPackageName()} instead. 78 */ 79 @Deprecated getAdServicesPackageName(@onNull Context context)80 public static String getAdServicesPackageName(@NonNull Context context) { 81 return getAdServicesPackageName(context, DEFAULT_LOG_TAG); 82 } 83 84 /** 85 * Kill the Adservices process. 86 * 87 * @param context the context used to get Adservices package name. 88 * @param logTag the tag used for logging 89 */ killAdservicesProcess(@onNull Context context, @NonNull String logTag)90 public static void killAdservicesProcess(@NonNull Context context, @NonNull String logTag) { 91 ShellUtils.runShellCommand( 92 "%s %s", FORCE_KILL_PROCESS_COMMAND, getAdServicesPackageName(context, logTag)); 93 94 try { 95 // Sleep 100 ms to allow AdServices process to recover 96 Thread.sleep(/* millis= */ 100); 97 } catch (InterruptedException ignored) { 98 Log.e(logTag, "Recovery from restarting AdServices process interrupted", ignored); 99 } 100 } 101 102 /** 103 * Kill the Adservices process. An overloading method of {@code killAdservicesProcess(context, 104 * logTag)} by using {@code DEFAULT_LOG_TAG}. 105 * 106 * @param context the context used to get Adservices package name. 107 */ killAdservicesProcess(@onNull Context context)108 public static void killAdservicesProcess(@NonNull Context context) { 109 killAdservicesProcess(context, DEFAULT_LOG_TAG); 110 } 111 112 /** 113 * Kill the Adservices process. An overloading method of {@code killAdservicesProcess(context, 114 * logTag)} by using Adservices package name directly. 115 * 116 * @param adservicesPackageName the Adservices package name. 117 */ killAdservicesProcess(@onNull String adservicesPackageName)118 public static void killAdservicesProcess(@NonNull String adservicesPackageName) { 119 ShellUtils.runShellCommand("%s %s", FORCE_KILL_PROCESS_COMMAND, adservicesPackageName); 120 } 121 122 /** 123 * Check whether the device is supported. Adservices doesn't support non-phone device. 124 * 125 * @return if the device is supported. 126 * @deprecated use {@link AdServicesDeviceSupportedRule} instead. 127 */ 128 @Deprecated 129 @SuppressWarnings("InlineMeSuggester") isDeviceSupported()130 public static boolean isDeviceSupported() { 131 return AdServicesSupportHelper.getInstance().isDeviceSupported(); 132 } 133 134 /** 135 * Checks if the device is debuggable, as the {@code Build.isDebuggable()} was just added on 136 * Android S. 137 */ isDebuggable()138 public static boolean isDebuggable() { 139 if (SdkLevel.isAtLeastS()) { 140 return Build.isDebuggable(); 141 } 142 return SystemProperties.getInt("ro.debuggable", 0) == 1; 143 } 144 145 /** 146 * Resolve package name of the active AdServices APK on this device. 147 * 148 * <p>Copied from AdServicesCommon. 149 */ resolveAdServicesService( List<ResolveInfo> intentResolveInfos, String intentAction, String logTag)150 private static ServiceInfo resolveAdServicesService( 151 List<ResolveInfo> intentResolveInfos, String intentAction, String logTag) { 152 if (intentResolveInfos == null || intentResolveInfos.isEmpty()) { 153 Log.e( 154 logTag, 155 "Failed to find resolveInfo for adServices service. Intent action: " 156 + intentAction); 157 return null; 158 } 159 160 // On T+ devices, we may have two versions of the services present due to b/263904312. 161 if (intentResolveInfos.size() > 2) { 162 StringBuilder intents = new StringBuilder(""); 163 for (ResolveInfo intentResolveInfo : intentResolveInfos) { 164 if (intentResolveInfo != null && intentResolveInfo.serviceInfo != null) { 165 intents.append(intentResolveInfo.serviceInfo.packageName); 166 } 167 } 168 Log.e(logTag, "Found multiple services " + intents + " for " + intentAction); 169 return null; 170 } 171 172 // On T+ devices, only use the service that comes from AdServices APK. The package name of 173 // AdService is com.[google.]android.adservices.api while the package name of ExtServices 174 // APK is com.[google.]android.ext.services. 175 ServiceInfo serviceInfo = null; 176 177 // We have already checked if there are 0 OR more than 2 services returned. 178 switch (intentResolveInfos.size()) { 179 case 2: 180 // In the case of 2, always use the one from AdServicesApk. 181 if (intentResolveInfos.get(0) != null 182 && intentResolveInfos.get(0).serviceInfo != null 183 && intentResolveInfos.get(0).serviceInfo.packageName != null 184 && intentResolveInfos 185 .get(0) 186 .serviceInfo 187 .packageName 188 .endsWith(ADSERVICES_APK_PACKAGE_NAME_SUFFIX)) { 189 serviceInfo = intentResolveInfos.get(0).serviceInfo; 190 } else if (intentResolveInfos.get(1) != null 191 && intentResolveInfos.get(1).serviceInfo != null 192 && intentResolveInfos.get(1).serviceInfo.packageName != null 193 && intentResolveInfos 194 .get(1) 195 .serviceInfo 196 .packageName 197 .endsWith(ADSERVICES_APK_PACKAGE_NAME_SUFFIX)) { 198 serviceInfo = intentResolveInfos.get(1).serviceInfo; 199 } 200 break; 201 202 case 1: 203 serviceInfo = intentResolveInfos.get(0).serviceInfo; 204 break; 205 } 206 return serviceInfo; 207 } 208 209 /** Install test app and verify the installation. */ installTestApp(String apkPath)210 public static void installTestApp(String apkPath) { 211 int currentUserId = Process.myUserHandle().getIdentifier(); 212 String installMessage = 213 ShellUtils.runShellCommand("pm install --user %d -r %s", currentUserId, apkPath); 214 assertThat(installMessage).contains("Success"); 215 } 216 217 /** Uninstall test app and verify the uninstallation. */ uninstallTestApp(String apkName)218 public static void uninstallTestApp(String apkName) { 219 int currentUserId = Process.myUserHandle().getIdentifier(); 220 String uninstallMessage = 221 ShellUtils.runShellCommand("pm uninstall --user %d %s", currentUserId, apkName); 222 assertThat(uninstallMessage).contains("Success"); 223 } 224 } 225