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.service.common; 18 19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_CANCEL_JOB_FAILURE; 20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_UPDATE_ACTIVITY_FAILURE; 21 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_UPDATE_SERVICE_FAILURE; 22 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_ENABLE_RECEIVER_FAILURE; 23 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_DISABLE_RECEIVER_FAILURE; 24 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__JOB_SCHEDULER_IS_UNAVAILABLE; 25 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON; 26 27 import android.app.job.JobInfo; 28 import android.app.job.JobScheduler; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.pm.PackageManager; 32 import android.os.Build; 33 34 import com.android.adservices.AdServicesCommon; 35 import com.android.adservices.LogUtil; 36 import com.android.adservices.errorlogging.ErrorLogUtil; 37 import com.android.adservices.service.Flags; 38 import com.android.adservices.service.FlagsFactory; 39 import com.android.adservices.service.common.compat.PackageManagerCompatUtils; 40 import com.android.adservices.shared.common.ApplicationContextSingleton; 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.modules.utils.build.SdkLevel; 43 44 import java.util.List; 45 import java.util.Objects; 46 import java.util.stream.Collectors; 47 48 /** Handles the Back Compat initialization for AdExtServices APK. */ 49 public final class AdServicesBackCompatInit { 50 private final Context mContext; 51 private final Flags mFlags; 52 53 @VisibleForTesting AdServicesBackCompatInit(Context context)54 AdServicesBackCompatInit(Context context) { 55 this.mContext = Objects.requireNonNull(context); 56 this.mFlags = FlagsFactory.getFlags(); 57 } 58 59 /** Gets an instance of {@link AdServicesBackCompatInit}. */ getInstance()60 public static AdServicesBackCompatInit getInstance() { 61 return new AdServicesBackCompatInit(ApplicationContextSingleton.get()); 62 } 63 64 /** 65 * Initialize back compat components in ExtServices package. Skips execution if executed within 66 * the AdServices package. 67 */ initializeComponents()68 public void initializeComponents() { 69 String packageName = mContext.getPackageName(); 70 if (isNullOrAdServicesPackageName(packageName)) { 71 // Package name is null or running within the AdServices package, so don't do anything. 72 LogUtil.d("Running within package %s, not changing component state", packageName); 73 return; 74 } 75 76 // On T+ devices, always disable the AdExtServices activities and services. 77 if (SdkLevel.isAtLeastT()) { 78 // If this is not an S- device, disable the activities, services, unregister the 79 // broadcast receivers, and unschedule any background jobs. 80 unregisterPackageChangedBroadcastReceivers(); 81 updateAdExtServicesActivities(/* shouldEnable= */ false); 82 updateAdExtServicesServices(/* shouldEnable= */ false); 83 disableScheduledBackgroundJobs(); 84 return; 85 } 86 87 // If this is an S- device but the flags are disabled, do nothing. 88 if (mFlags.getEnableBackCompat() 89 && mFlags.getAdServicesEnabled() 90 && !mFlags.getGlobalKillSwitch()) { 91 registerPackagedChangedBroadcastReceivers(); 92 updateAdExtServicesActivities(/* shouldEnable= */ true); 93 updateAdExtServicesServices(/* shouldEnable= */ true); 94 return; 95 } 96 97 LogUtil.d("Exiting AdServicesBackCompatInit because flags are disabled"); 98 } 99 100 /** 101 * Cancels all scheduled jobs if running within the ExtServices APK. Needed because we could 102 * have some persistent jobs that were scheduled on S before an OTA to T. 103 */ disableScheduledBackgroundJobs()104 private void disableScheduledBackgroundJobs() { 105 try { 106 JobScheduler scheduler = mContext.getSystemService(JobScheduler.class); 107 if (scheduler == null) { 108 LogUtil.e("Could not retrieve JobScheduler instance, so not cancelling jobs"); 109 ErrorLogUtil.e( 110 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__JOB_SCHEDULER_IS_UNAVAILABLE, 111 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 112 return; 113 } 114 for (JobInfo jobInfo : scheduler.getAllPendingJobs()) { 115 String jobClassName = jobInfo.getService().getClassName(); 116 // Cancel jobs from AdServices only 117 if (jobClassName.startsWith(AdServicesCommon.ADSERVICES_CLASS_PATH_PREFIX)) { 118 int jobId = jobInfo.getId(); 119 LogUtil.d("Deleting ext AdServices job %d %s", jobId, jobClassName); 120 scheduler.cancel(jobId); 121 } 122 } 123 LogUtil.d( 124 "All AdServices scheduled jobs cancelled on package %s", 125 mContext.getPackageName()); 126 } catch (Exception e) { 127 LogUtil.e(e, "Error when cancelling scheduled jobs"); 128 ErrorLogUtil.e( 129 e, 130 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_CANCEL_JOB_FAILURE, 131 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 132 } 133 } 134 135 /** 136 * Registers a receiver for any broadcasts regarding changes to any packages for all users on 137 * the device at boot up. After receiving the broadcast, send an explicit broadcast to the 138 * AdServices module as that user. 139 */ 140 @SuppressWarnings("NewApi") registerPackagedChangedBroadcastReceivers()141 private void registerPackagedChangedBroadcastReceivers() { 142 boolean result = PackageChangedReceiver.enableReceiver(mContext, mFlags); 143 LogUtil.d( 144 "Package Change Receiver registration: Success=%s, Package=%s", 145 result, mContext.getPackageName()); 146 if (!result) { 147 ErrorLogUtil.e( 148 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_ENABLE_RECEIVER_FAILURE, 149 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 150 } 151 } 152 153 @SuppressWarnings("NewApi") unregisterPackageChangedBroadcastReceivers()154 private void unregisterPackageChangedBroadcastReceivers() { 155 boolean result = PackageChangedReceiver.disableReceiver(mContext, mFlags); 156 LogUtil.d( 157 "Package Change Receiver unregistration: Success=%s, Package=%s", 158 result, mContext.getPackageName()); 159 if (!result) { 160 ErrorLogUtil.e( 161 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_DISABLE_RECEIVER_FAILURE, 162 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 163 } 164 } 165 166 /** 167 * Activities for user consent and control are disabled by default. Only on S- devices, after 168 * the flag is enabled, we enable the activities. 169 */ updateAdExtServicesActivities(boolean shouldEnable)170 private void updateAdExtServicesActivities(boolean shouldEnable) { 171 try { 172 updateComponents(PackageManagerCompatUtils.CONSENT_ACTIVITIES_CLASSES, shouldEnable); 173 LogUtil.d("Updated state of AdExtServices activities: [enabled=%s]", shouldEnable); 174 } catch (IllegalArgumentException e) { 175 LogUtil.e("Error when updating activities: %s", e.getMessage()); 176 ErrorLogUtil.e( 177 e, 178 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_UPDATE_ACTIVITY_FAILURE, 179 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 180 } 181 } 182 183 /** 184 * Disables services that have intent filters specified in the AdExtServicesManifest to prevent 185 * duplicates on T+ devices, Conversely, it enables these services on devices running versions S 186 * and below. 187 */ updateAdExtServicesServices(boolean shouldEnable)188 private void updateAdExtServicesServices(boolean shouldEnable) { 189 try { 190 int currentSdkInt = getSdkLevelInt(); 191 List<String> servicesToUpdate = 192 PackageManagerCompatUtils.SERVICE_CLASSES_AND_MIN_SDK_SUPPORT_PAIRS.stream() 193 .filter(p -> p.second <= currentSdkInt) 194 .map(p -> p.first) 195 .collect(Collectors.toList()); 196 updateComponents(servicesToUpdate, shouldEnable); 197 LogUtil.d("Updated state of AdExtServices services: [enable=%s]", shouldEnable); 198 } catch (IllegalArgumentException e) { 199 LogUtil.e("Error when updating services: %s", e.getMessage()); 200 ErrorLogUtil.e( 201 e, 202 AD_SERVICES_ERROR_REPORTED__ERROR_CODE__BACK_COMPAT_INIT_UPDATE_SERVICE_FAILURE, 203 AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON); 204 } 205 } 206 207 @VisibleForTesting updateComponents(List<String> components, boolean shouldEnable)208 void updateComponents(List<String> components, boolean shouldEnable) { 209 PackageManager packageManager = mContext.getPackageManager(); 210 String packageName = mContext.getPackageName(); 211 for (String component : components) { 212 packageManager.setComponentEnabledSetting( 213 new ComponentName(packageName, component), 214 shouldEnable 215 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 216 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 217 PackageManager.DONT_KILL_APP); 218 } 219 } 220 isNullOrAdServicesPackageName(String packageName)221 private boolean isNullOrAdServicesPackageName(String packageName) { 222 return packageName == null 223 || packageName.endsWith(AdServicesCommon.ADSERVICES_APK_PACKAGE_NAME_SUFFIX); 224 } 225 226 @VisibleForTesting getSdkLevelInt()227 static int getSdkLevelInt() { 228 return Build.VERSION.SDK_INT; 229 } 230 } 231