• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.ondevicepersonalization.services.manifest;
18 
19 import static android.content.pm.PackageManager.GET_META_DATA;
20 
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.res.Resources;
26 import android.content.res.XmlResourceParser;
27 
28 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
29 import com.android.ondevicepersonalization.services.enrollment.PartnerEnrollmentChecker;
30 
31 import com.google.common.collect.ImmutableList;
32 
33 /**
34  * Helper class for parsing and checking app manifest configs
35  */
36 public final class AppManifestConfigHelper {
37     private static final String ON_DEVICE_PERSONALIZATION_CONFIG_PROPERTY =
38             "android.ondevicepersonalization.ON_DEVICE_PERSONALIZATION_CONFIG";
39     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
40     private static final String TAG = AppManifestConfigHelper.class.getSimpleName();
41 
AppManifestConfigHelper()42     private AppManifestConfigHelper() {
43     }
44 
45     /**
46      * Determines if the given package's manifest contains ODP settings
47      *
48      * @param context the context of the API call.
49      * @param packageName the packageName of the package whose manifest config will be read
50      * @return true if the ODP setting exists, false otherwise
51      */
manifestContainsOdpSettings(Context context, String packageName)52     public static boolean manifestContainsOdpSettings(Context context, String packageName) {
53         PackageManager pm = context.getPackageManager();
54         try {
55             pm.getProperty(ON_DEVICE_PERSONALIZATION_CONFIG_PROPERTY, packageName);
56         } catch (PackageManager.NameNotFoundException e) {
57             return false;
58         }
59         return true;
60     }
61 
62     /**
63      * Returns the ODP manifest config for a package.
64      *
65      * <p>Throws a {@link RuntimeException} if the package, its ODP settings are not found or cannot
66      * be parsed.
67      */
68     /** Returns the ODP manifest config for a package. */
getAppManifestConfig(Context context, String packageName)69     public static AppManifestConfig getAppManifestConfig(Context context, String packageName) {
70         if (!manifestContainsOdpSettings(context, packageName)) {
71             // TODO(b/241941021) Determine correct exception to throw
72             throw new IllegalArgumentException(
73                     "OdpSettings not found for package: " + packageName.toString());
74         }
75         PackageManager pm = context.getPackageManager();
76         try {
77             int resId = pm.getProperty(ON_DEVICE_PERSONALIZATION_CONFIG_PROPERTY,
78                     packageName).getResourceId();
79             Resources resources = pm.getResourcesForApplication(packageName);
80             XmlResourceParser xmlParser = resources.getXml(resId);
81             // TODO(b/239479120) Update to avoid re-parsing the XML too frequently if required
82             return AppManifestConfigParser.getConfig(xmlParser);
83         } catch (Exception e) {
84             // TODO(b/241941021) Determine correct exception to throw
85             throw new IllegalArgumentException(
86                     "Failed to parse manifest for package: " + packageName, e);
87         }
88     }
89 
90     /**
91      * Gets the download URL from package's ODP settings config
92      *
93      * @param context     the context of the API call.
94      * @param packageName the packageName of the package whose manifest config will be read
95      */
getDownloadUrlFromOdpSettings(Context context, String packageName)96     public static String getDownloadUrlFromOdpSettings(Context context, String packageName) {
97         return getAppManifestConfig(context, packageName).getDownloadUrl();
98     }
99 
100     /**
101      * Gets the service name from package's ODP settings config
102      *
103      * @param context     the context of the API call.
104      * @param packageName the packageName of the package whose manifest config will be read
105      */
getServiceNameFromOdpSettings(Context context, String packageName)106     public static String getServiceNameFromOdpSettings(Context context,
107             String packageName) {
108         return getAppManifestConfig(context, packageName).getServiceName();
109     }
110 
111     /**
112      * Gets the federated compute service remote server url from package's ODP settings config
113      *
114      * @param context     the context of the API call.
115      * @param packageName the packageName of the package whose manifest config will be read
116      */
getFcRemoteServerUrlFromOdpSettings(Context context, String packageName)117     public static String getFcRemoteServerUrlFromOdpSettings(Context context,
118             String packageName) {
119         return getAppManifestConfig(context, packageName).getFcRemoteServerUrl();
120     }
121 
122     /**
123      * Get the list of packages enrolled for ODP, by checking manifest for ODP settings.
124      *
125      * @param context The context of the calling process.
126      * @param enrolledOnly Whether to only include packages that pass the enrollment check.
127      * @return The list of packages that contain ODP manifest settings (and potentially enrolled)
128      */
getOdpPackages(Context context, boolean enrolledOnly)129     public static ImmutableList<String> getOdpPackages(Context context, boolean enrolledOnly) {
130         ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
131         for (PackageInfo packageInfo :
132                 context.getPackageManager()
133                         .getInstalledPackages(PackageManager.PackageInfoFlags.of(GET_META_DATA))) {
134             String packageName = packageInfo.packageName;
135 
136             if (manifestContainsOdpSettings(context, packageName)) {
137                 if (enrolledOnly
138                         && !PartnerEnrollmentChecker.isIsolatedServiceEnrolled(packageName)) {
139                     sLogger.d(TAG + ": package %s has ODP manifest, but not enrolled", packageName);
140                     continue;
141                 }
142 
143                 String enrolledString = enrolledOnly ? "and is enrolled" : "";
144                 sLogger.d(TAG + ": package %s has ODP manifest " + enrolledString, packageName);
145                 builder.add(packageName);
146             }
147         }
148         return builder.build();
149     }
150 
151     /**
152      * Get the list of services enrolled for ODP.
153      *
154      * @param context The context of the calling process.
155      * @param enrolledOnly Whether to only include services that pass the enrollment check.
156      * @return The list of matching Services with ODP manifest settings (and are potentially
157      *     enrolled).
158      */
getOdpServices( Context context, boolean enrolledOnly)159     public static ImmutableList<ComponentName> getOdpServices(
160             Context context, boolean enrolledOnly) {
161         ImmutableList.Builder<ComponentName> builder = new ImmutableList.Builder<>();
162         for (PackageInfo packageInfo :
163                 context.getPackageManager()
164                         .getInstalledPackages(PackageManager.PackageInfoFlags.of(GET_META_DATA))) {
165             String packageName = packageInfo.packageName;
166 
167             if (manifestContainsOdpSettings(context, packageName)) {
168                 if (enrolledOnly
169                         && !PartnerEnrollmentChecker.isIsolatedServiceEnrolled(packageName)) {
170                     sLogger.d(TAG + ": service %s has ODP manifest, but not enrolled", packageName);
171                     continue;
172                 }
173 
174                 String enrolledString = enrolledOnly ? "and is enrolled" : "";
175                 sLogger.d(TAG + ": service %s has ODP manifest " + enrolledString, packageName);
176 
177                 String serviceClass = getServiceNameFromOdpSettings(context, packageName);
178                 ComponentName service = ComponentName.createRelative(packageName, serviceClass);
179                 builder.add(service);
180             }
181         }
182         return builder.build();
183     }
184 }
185