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 android.ext.services.common; 18 19 import android.annotation.SuppressLint; 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.content.pm.ResolveInfo; 27 import android.os.Build; 28 import android.provider.DeviceConfig; 29 import android.util.Log; 30 31 import androidx.annotation.ChecksSdkIntAtLeast; 32 import androidx.annotation.NonNull; 33 import androidx.annotation.VisibleForTesting; 34 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.Objects; 38 39 /** 40 * Handles the BootCompleted initialization for AdExtServices APK on S-. 41 * The BootCompleted receiver re-broadcasts a different intent that is handled by the 42 * AdExtBootCompletedReceiver within the AdServices apk. The reason for doing this here instead of 43 * within the AdServices APK is due to problematic platform modifications (b/286070595). 44 */ 45 public class BootCompletedReceiver extends BroadcastReceiver { 46 private static final String TAG = "extservices"; 47 private static final String KEY_PRIVACY_EXCLUDE_LIST = "privacy_exclude_list"; 48 private static final String KEY_EXTSERVICES_BOOT_COMPLETE_RECEIVER = 49 "extservices_bootcomplete_enabled"; 50 51 private static final String ADEXTBOOTCOMPLETEDRECEIVER_CLASS_NAME = 52 "com.android.adservices.service.common.AdExtBootCompletedReceiver"; 53 private static final String REBROADCAST_INTENT_ACTION = 54 "android.adservices.action.INIT_EXT_SERVICES"; 55 private static final String ADSERVICES_SETTINGS_MAINACTIVITY = 56 "com.android.adservices.ui.settings.activities.AdServicesSettingsMainActivity"; 57 58 @SuppressLint("MissingPermission") 59 @Override onReceive(Context context, Intent intent)60 public void onReceive(Context context, Intent intent) { 61 Log.i(TAG, "BootCompletedReceiver received BOOT_COMPLETED broadcast (f): " 62 + Build.FINGERPRINT); 63 64 // Check if the feature is enabled, otherwise exit without doing anything. 65 if (!isReceiverEnabled()) { 66 Log.d(TAG, "BootCompletedReceiver not enabled in config, exiting"); 67 return; 68 } 69 70 String adServicesPackageName = getAdExtServicesPackageName(context); 71 if (adServicesPackageName == null) { 72 Log.d(TAG, "AdServices package was not present, exiting BootCompletedReceiver"); 73 return; 74 } 75 76 // No need to run this on every boot if we're on T+ and the AdExtServices components have 77 // already been disabled. 78 if (shouldDisableReceiver(context, adServicesPackageName)) { 79 context.getPackageManager().setComponentEnabledSetting( 80 new ComponentName(context.getPackageName(), this.getClass().getName()), 81 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 82 0); 83 Log.d(TAG, "Disabled BootCompletedReceiver as AdServices is already initialized."); 84 return; 85 } 86 87 // Check if this device is among a list of excluded devices 88 String excludeList = getExcludedFingerprints(); 89 Log.d(TAG, "Read BOOT_COMPLETED broadcast exclude list: " + excludeList); 90 if (Arrays.stream(excludeList.split(",")) 91 .map(String::trim) 92 .filter(s -> !s.isEmpty()) 93 .anyMatch(Build.FINGERPRINT::startsWith)) { 94 Log.d(TAG, "Device is present in the exclude list, exiting BootCompletedReceiver"); 95 return; 96 } 97 98 // Re-broadcast the intent 99 Intent intentToSend = new Intent(REBROADCAST_INTENT_ACTION); 100 intentToSend.setComponent( 101 new ComponentName(adServicesPackageName, ADEXTBOOTCOMPLETEDRECEIVER_CLASS_NAME)); 102 intentToSend.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); 103 context.sendBroadcast(intentToSend); 104 Log.i(TAG, "BootCompletedReceiver sending init broadcast: " + intentToSend); 105 } 106 107 @SuppressLint("MissingPermission") 108 @VisibleForTesting isReceiverEnabled()109 public boolean isReceiverEnabled() { 110 return DeviceConfig.getBoolean( 111 DeviceConfig.NAMESPACE_ADSERVICES, 112 /* flagName */ KEY_EXTSERVICES_BOOT_COMPLETE_RECEIVER, 113 /* defaultValue */ false); 114 } 115 116 @SuppressLint("MissingPermission") 117 @VisibleForTesting getExcludedFingerprints()118 public String getExcludedFingerprints() { 119 return DeviceConfig.getString( 120 DeviceConfig.NAMESPACE_ADSERVICES, 121 /* flagName */ KEY_PRIVACY_EXCLUDE_LIST, 122 /* defaultValue */ ""); 123 } 124 125 @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) 126 @VisibleForTesting isAtLeastT()127 public boolean isAtLeastT() { 128 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; 129 } 130 shouldDisableReceiver(@onNull Context context, @NonNull String adServicesPackageName)131 private boolean shouldDisableReceiver(@NonNull Context context, 132 @NonNull String adServicesPackageName) { 133 Objects.requireNonNull(context); 134 Objects.requireNonNull(adServicesPackageName); 135 return isAtLeastT() && !isExtServicesInitialized(context, adServicesPackageName); 136 } 137 isExtServicesInitialized(Context context, String adServicesPackageName)138 private boolean isExtServicesInitialized(Context context, String adServicesPackageName) { 139 Intent intent = new Intent(); 140 intent.setComponent( 141 new ComponentName(adServicesPackageName, ADSERVICES_SETTINGS_MAINACTIVITY)); 142 List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent, 143 PackageManager.MATCH_DEFAULT_ONLY); 144 Log.d(TAG, "Components matching AdServicesSettingsMainActivity: " + list); 145 return list != null && !list.isEmpty(); 146 } 147 getAdExtServicesPackageName(@onNull Context context)148 private String getAdExtServicesPackageName(@NonNull Context context) { 149 Objects.requireNonNull(context); 150 151 List<PackageInfo> installedPackages = 152 context.getPackageManager().getInstalledPackages(PackageManager.MATCH_SYSTEM_ONLY); 153 154 return installedPackages.stream() 155 .filter(s -> s.packageName.endsWith("android.ext.adservices.api")) 156 .map(s -> s.packageName) 157 .findFirst() 158 .orElse(null); 159 } 160 } 161