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 android.app.job.JobScheduler; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.os.Build; 27 28 import androidx.annotation.NonNull; 29 import androidx.annotation.RequiresApi; 30 31 import com.android.adservices.AdServicesCommon; 32 import com.android.adservices.LogUtil; 33 import com.android.adservices.service.FlagsFactory; 34 import com.android.adservices.service.common.compat.PackageManagerCompatUtils; 35 import com.android.internal.annotations.VisibleForTesting; 36 37 import java.util.List; 38 import java.util.Objects; 39 40 /** Handles the BootCompleted initialization for AdExtServices APK on S-. */ 41 // TODO(b/269798827): Enable for R. 42 // TODO(b/274675141): add e2e test for boot complete receiver 43 @RequiresApi(Build.VERSION_CODES.S) 44 public class AdExtBootCompletedReceiver extends BroadcastReceiver { 45 46 @Override onReceive(Context context, Intent intent)47 public void onReceive(Context context, Intent intent) { 48 // TODO(b/269798827): Enable for R. 49 // On T+ devices, always disable the AdExtServices activities and services. 50 if (Build.VERSION.SDK_INT != Build.VERSION_CODES.S 51 && Build.VERSION.SDK_INT != Build.VERSION_CODES.S_V2) { 52 // If this is not an S- device, disable the activities, services, unregister the 53 // broadcast receivers, and unschedule any background jobs. 54 unregisterPackageChangedBroadcastReceivers(context); 55 updateAdExtServicesActivities(context, /* shouldEnable= */ false); 56 updateAdExtServicesServices(context, /* shouldEnable= */ false); 57 disableScheduledBackgroundJobs(context); 58 return; 59 } 60 61 // If this is an S- device but the flags are disabled, do nothing. 62 if (!FlagsFactory.getFlags().getEnableBackCompat() 63 || !FlagsFactory.getFlags().getAdServicesEnabled() 64 || FlagsFactory.getFlags().getGlobalKillSwitch()) { 65 return; 66 } 67 68 registerPackagedChangedBroadcastReceivers(context); 69 updateAdExtServicesActivities(context, /* shouldEnable= */ true); 70 updateAdExtServicesServices(context, /* shouldEnable= */ true); 71 } 72 73 /** 74 * Cancels all scheduled jobs if running within the ExtServices APK. Needed because we could 75 * have some persistent jobs that were scheduled on S before an OTA to T. 76 */ 77 @VisibleForTesting disableScheduledBackgroundJobs(@onNull Context context)78 void disableScheduledBackgroundJobs(@NonNull Context context) { 79 Objects.requireNonNull(context); 80 81 try { 82 String packageName = getPackageName(context); 83 if (packageName == null 84 || packageName.endsWith(AdServicesCommon.ADSERVICES_APK_PACKAGE_NAME_SUFFIX)) { 85 // Running within the AdServices package, so don't do anything. 86 LogUtil.d("Running within AdServices package, not changing scheduled job state"); 87 return; 88 } 89 90 JobScheduler scheduler = context.getSystemService(JobScheduler.class); 91 if (scheduler == null) { 92 LogUtil.d("Could not retrieve JobScheduler instance, so not cancelling jobs"); 93 return; 94 } 95 96 scheduler.cancelAll(); 97 LogUtil.d("All scheduled jobs cancelled on package %s", packageName); 98 } catch (Exception e) { 99 LogUtil.e(e, "Error when cancelling scheduled jobs"); 100 e.printStackTrace(); 101 } 102 } 103 104 /** 105 * Registers a receiver for any broadcasts regarding changes to any packages for all users on 106 * the device at boot up. After receiving the broadcast, send an explicit broadcast to the 107 * AdServices module as that user. 108 */ 109 @VisibleForTesting registerPackagedChangedBroadcastReceivers(Context context)110 void registerPackagedChangedBroadcastReceivers(Context context) { 111 PackageChangedReceiver.enableReceiver(context, FlagsFactory.getFlags()); 112 LogUtil.d( 113 "Package changed broadcast receivers registered from package %s", 114 context.getPackageName()); 115 } 116 117 @VisibleForTesting unregisterPackageChangedBroadcastReceivers(Context context)118 void unregisterPackageChangedBroadcastReceivers(Context context) { 119 PackageChangedReceiver.disableReceiver(context, FlagsFactory.getFlags()); 120 LogUtil.d( 121 "Package change broadcast receivers unregistered from package %s", 122 context.getPackageName()); 123 } 124 125 /** 126 * Activities for user consent and control are disabled by default. Only on S- devices, after 127 * the flag is enabled, we enable the activities. 128 */ 129 @VisibleForTesting updateAdExtServicesActivities(@onNull Context context, boolean shouldEnable)130 void updateAdExtServicesActivities(@NonNull Context context, boolean shouldEnable) { 131 Objects.requireNonNull(context); 132 133 try { 134 String packageName = getPackageName(context); 135 updateComponents( 136 context, 137 PackageManagerCompatUtils.CONSENT_ACTIVITIES_CLASSES, 138 packageName, 139 shouldEnable); 140 LogUtil.d("Updated state of AdExtServices activities: [enabled=" + shouldEnable + "]"); 141 } catch (Exception e) { 142 LogUtil.e("Error when updating activities: " + e.getMessage()); 143 e.printStackTrace(); 144 } 145 } 146 147 /** 148 * Disables services with intent filters defined in AdExtServicesManifest to avoid dupes on T+ 149 * devices, or enables the same services on S to make sure they are re-enabled after OTA from R. 150 */ 151 @VisibleForTesting updateAdExtServicesServices(@onNull Context context, boolean shouldEnable)152 void updateAdExtServicesServices(@NonNull Context context, boolean shouldEnable) { 153 Objects.requireNonNull(context); 154 155 try { 156 String packageName = getPackageName(context); 157 updateComponents( 158 context, PackageManagerCompatUtils.SERVICE_CLASSES, packageName, shouldEnable); 159 LogUtil.d("Updated state of AdExtServices services: [enable=" + shouldEnable + "]"); 160 } catch (Exception e) { 161 LogUtil.e("Error when updating services: " + e.getMessage()); 162 e.printStackTrace(); 163 } 164 } 165 166 @VisibleForTesting updateComponents( @onNull Context context, @NonNull List<String> components, @NonNull String adServicesPackageName, boolean shouldEnable)167 static void updateComponents( 168 @NonNull Context context, 169 @NonNull List<String> components, 170 @NonNull String adServicesPackageName, 171 boolean shouldEnable) { 172 Objects.requireNonNull(context); 173 Objects.requireNonNull(components); 174 Objects.requireNonNull(adServicesPackageName); 175 if (adServicesPackageName.contains(AdServicesCommon.ADSERVICES_APK_PACKAGE_NAME_SUFFIX)) { 176 throw new IllegalStateException( 177 "Components for package with AdServices APK package suffix should not be " 178 + "updated!"); 179 } 180 181 PackageManager packageManager = context.getPackageManager(); 182 for (String component : components) { 183 packageManager.setComponentEnabledSetting( 184 new ComponentName(adServicesPackageName, component), 185 shouldEnable 186 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 187 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 188 PackageManager.DONT_KILL_APP); 189 } 190 } 191 getPackageName(Context context)192 private String getPackageName(Context context) throws PackageManager.NameNotFoundException { 193 PackageManager packageManager = context.getPackageManager(); 194 PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 195 return packageInfo.packageName; 196 } 197 } 198