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