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.compat;
18 
19 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PACKAGE_NAME_NOT_FOUND_EXCEPTION;
20 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON;
21 
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageInfo;
26 import android.content.pm.PackageManager;
27 import android.os.Build;
28 import android.util.Pair;
29 
30 import androidx.annotation.NonNull;
31 
32 import com.android.adservices.AdServicesCommon;
33 import com.android.adservices.LogUtil;
34 import com.android.adservices.errorlogging.ErrorLogUtil;
35 import com.android.modules.utils.build.SdkLevel;
36 
37 import com.google.common.collect.ImmutableList;
38 
39 import java.util.List;
40 import java.util.Objects;
41 
42 /** Utility class for compatibility of PackageManager APIs with Android S and earlier. */
43 public final class PackageManagerCompatUtils {
44 
PackageManagerCompatUtils()45     private PackageManagerCompatUtils() {
46         // Prevent instantiation
47     }
48 
49     // This list is the same as the list declared in the AdExtServicesManifest, where the
50     // activities need need to be enabled/disabled based on flag settings and SDK version.
51     // TODO(b/263904312): Remove after max_sdk_version is implemented.
52     // TODO(b/272737642) scan activities instead of hardcode
53     // LINT.IfChange(activities_and_services)
54     public static final ImmutableList<String> CONSENT_ACTIVITIES_CLASSES =
55             ImmutableList.of(
56                     "com.android.adservices.ui.settings.activities."
57                             + "AdServicesSettingsMainActivity",
58                     "com.android.adservices.ui.settings.activities.TopicsActivity",
59                     "com.android.adservices.ui.settings.activities.BlockedTopicsActivity",
60                     "com.android.adservices.ui.settings.activities.AppsActivity",
61                     "com.android.adservices.ui.settings.activities.BlockedAppsActivity",
62                     "com.android.adservices.ui.settings.activities.MeasurementActivity",
63                     "com.android.adservices.ui.notifications.ConsentNotificationActivity");
64 
65     // This list is the same as the list declared in the AdExtServicesManifest, where the
66     // services with intent filters need to be enabled/disabled based on flag settings and SDK
67     // version. The list is a collection of pairs where the first value is the name of the service,
68     // and the second value is the min SDK version for which the service is supported.
69     // TODO(b/263904312): Remove after max_sdk_version is implemented.
70     // TODO(b/272737642) scan services instead of hardcode
71     public static final ImmutableList<Pair<String, Integer>>
72             SERVICE_CLASSES_AND_MIN_SDK_SUPPORT_PAIRS =
73                     ImmutableList.of(
74                             new Pair<>(
75                                     /* service= */ "com.android.adservices.adid.AdIdService",
76                                     /* minSdkSupport= */ Build.VERSION_CODES.R),
77                             new Pair<>(
78                                     /* service= */ "com.android.adservices.measurement.MeasurementService",
79                                     /* minSdkSupport= */ Build.VERSION_CODES.R),
80                             new Pair<>(
81                                     /* service= */ "com.android.adservices.common.AdServicesCommonService",
82                                     /* minSdkSupport= */ Build.VERSION_CODES.R),
83                             new Pair<>(
84                                     /* service= */ "com.android.adservices.adselection.AdSelectionService",
85                                     /* minSdkSupport= */ Build.VERSION_CODES.S),
86                             new Pair<>(
87                                     /* service= */ "com.android.adservices.customaudience.CustomAudienceService",
88                                     /* minSdkSupport= */ Build.VERSION_CODES.S),
89                             new Pair<>(
90                                     /* service= */ "android.adservices.signals.ProtectedSignalsService",
91                                     /* minSdkSupport= */ Build.VERSION_CODES.S),
92                             new Pair<>(
93                                     /* service= */ "com.android.adservices.topics.TopicsService",
94                                     /* minSdkSupport= */ Build.VERSION_CODES.S),
95                             new Pair<>(
96                                     /* service= */ "com.android.adservices.appsetid.AppSetIdService",
97                                     /* minSdkSupport= */ Build.VERSION_CODES.S));
98 
99     // LINT.ThenChange()
100 
101     /**
102      * Invokes the appropriate overload of {@code getInstalledPackages} on {@link PackageManager}
103      * depending on the SDK version.
104      *
105      * <p>{@code PackageInfoFlags.of()} actually takes a {@code long} as input whereas the earlier
106      * overload takes an {@code int}. For backward-compatibility, we're limited to the {@code int}
107      * range, so using {@code int} as a parameter to this method.
108      *
109      * @param packageManager the package manager instance to query
110      * @param flags the flags to be used for querying package manager
111      * @return the list of installed packages returned from the query to {@link PackageManager}
112      */
113     @NonNull
getInstalledPackages( @onNull PackageManager packageManager, int flags)114     public static List<PackageInfo> getInstalledPackages(
115             @NonNull PackageManager packageManager, int flags) {
116         Objects.requireNonNull(packageManager);
117         return SdkLevel.isAtLeastT()
118                 ? packageManager.getInstalledPackages(PackageManager.PackageInfoFlags.of(flags))
119                 : packageManager.getInstalledPackages(flags);
120     }
121 
122     /**
123      * Invokes the appropriate overload of {@code getInstalledApplications} on {@link
124      * PackageManager} depending on the SDK version.
125      *
126      * <p>{@code ApplicationInfoFlags.of()} actually takes a {@code long} as input whereas the
127      * earlier overload takes an {@code int}. For backward-compatibility, we're limited to the
128      * {@code int} range, so using {@code int} as a parameter to this method.
129      *
130      * @param packageManager the package manager instance to query
131      * @param flags the flags to be used for querying package manager
132      * @return the list of installed applications returned from the query to {@link PackageManager}
133      */
134     @NonNull
getInstalledApplications( @onNull PackageManager packageManager, int flags)135     public static List<ApplicationInfo> getInstalledApplications(
136             @NonNull PackageManager packageManager, int flags) {
137         Objects.requireNonNull(packageManager);
138         return SdkLevel.isAtLeastT()
139                 ? packageManager.getInstalledApplications(
140                         PackageManager.ApplicationInfoFlags.of(flags))
141                 : packageManager.getInstalledApplications(flags);
142     }
143 
144     /**
145      * Invokes the appropriate overload of {@code getApplicationInfo} on {@link PackageManager}
146      * depending on the SDK version.
147      *
148      * <p>{@code ApplicationInfoFlags.of()} actually takes a {@code long} as input whereas the
149      * earlier overload takes an {@code int}. For backward-compatibility, we're limited to the
150      * {@code int} range, so using {@code int} as a parameter to this method.
151      *
152      * @param packageManager the package manager instance to query
153      * @param flags the flags to be used for querying package manager
154      * @param packageName the name of the package for which the ApplicationInfo should be retrieved
155      * @return the application info returned from the query to {@link PackageManager}
156      */
157     @NonNull
getApplicationInfo( @onNull PackageManager packageManager, @NonNull String packageName, int flags)158     public static ApplicationInfo getApplicationInfo(
159             @NonNull PackageManager packageManager, @NonNull String packageName, int flags)
160             throws PackageManager.NameNotFoundException {
161         Objects.requireNonNull(packageManager);
162         Objects.requireNonNull(packageName);
163         return SdkLevel.isAtLeastT()
164                 ? packageManager.getApplicationInfo(
165                         packageName, PackageManager.ApplicationInfoFlags.of(flags))
166                 : packageManager.getApplicationInfo(packageName, flags);
167     }
168 
169     /**
170      * Invokes the appropriate overload of {@code getPackageUid} on {@link PackageManager} depending
171      * on the SDK version.
172      *
173      * <p>{@code PackageInfoFlags.of()} actually takes a {@code long} as input whereas the earlier
174      * overload takes an {@code int}. For backward-compatibility, we're limited to the {@code int}
175      * range, so using {@code int} as a parameter to this method.
176      *
177      * @param packageManager the packageManager instance to query
178      * @param packageName the name of the package for which the uid needs to be returned
179      * @param flags the flags to be used for querying the packageManager
180      * @return the uid of the package with the specified name
181      * @throws PackageManager.NameNotFoundException if the package was not found
182      */
getPackageUid( @onNull PackageManager packageManager, @NonNull String packageName, int flags)183     public static int getPackageUid(
184             @NonNull PackageManager packageManager, @NonNull String packageName, int flags)
185             throws PackageManager.NameNotFoundException {
186         Objects.requireNonNull(packageManager);
187         Objects.requireNonNull(packageName);
188         return SdkLevel.isAtLeastT()
189                 ? packageManager.getPackageUid(
190                         packageName, PackageManager.PackageInfoFlags.of(flags))
191                 : packageManager.getPackageUid(packageName, flags);
192     }
193 
194     /**
195      * Check whether the activities for user consent and control are enabled
196      *
197      * @param context the context
198      * @return true if AdServices activities are enabled, otherwise false
199      */
200     @NonNull
isAdServicesActivityEnabled(@onNull Context context)201     public static boolean isAdServicesActivityEnabled(@NonNull Context context) {
202         Objects.requireNonNull(context);
203         String packageName = context.getPackageName();
204         if (packageName == null) {
205             return false;
206         }
207 
208         // Activities are enabled by default in AdServices package
209         if (packageName.endsWith(AdServicesCommon.ADSERVICES_APK_PACKAGE_NAME_SUFFIX)) {
210             return true;
211         }
212         PackageManager packageManager = context.getPackageManager();
213         try {
214             PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
215             for (String activity : CONSENT_ACTIVITIES_CLASSES) {
216                 int componentEnabledState =
217                         packageManager.getComponentEnabledSetting(
218                                 new ComponentName(packageInfo.packageName, activity));
219                 // Activities are disabled by default in ExtServices package
220                 if (componentEnabledState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
221                     return false;
222                 }
223             }
224         } catch (PackageManager.NameNotFoundException e) {
225             LogUtil.e("Error when checking if activities are enabled: " + e.getMessage());
226             ErrorLogUtil.e(
227                     e,
228                     AD_SERVICES_ERROR_REPORTED__ERROR_CODE__PACKAGE_NAME_NOT_FOUND_EXCEPTION,
229                     AD_SERVICES_ERROR_REPORTED__PPAPI_NAME__COMMON);
230             return false;
231         }
232         return true;
233     }
234 }
235