• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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