• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.permissioncontroller.role.model;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.os.Build;
26 import android.os.Bundle;
27 import android.os.Process;
28 import android.os.UserHandle;
29 import android.util.ArraySet;
30 
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 
34 import com.android.modules.utils.build.SdkLevel;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Objects;
39 
40 /**
41  * Specifies a required component for an application to qualify for a {@link Role}.
42  */
43 public abstract class RequiredComponent {
44 
45     /**
46      * The {@code Intent} or {@code IntentFilter} data to match the components.
47      */
48     @NonNull
49     private final IntentFilterData mIntentFilterData;
50 
51     /**
52      * The minimum target SDK version for this component to be required.
53      * <p>
54      * This also implies a minimum platform SDK version for this component to be required.
55      */
56     private final int mMinTargetSdkVersion;
57 
58     /**
59      * Optional permission required on a component for match to succeed.
60      *
61      * @see android.content.pm.ActivityInfo#permission
62      * @see android.content.pm.ServiceInfo#permission
63      */
64     @Nullable
65     private final String mPermission;
66 
67     /**
68      * The query flags to match the components with.
69      */
70     private final int mQueryFlags;
71 
72     /**
73      * The meta data required on a component for match to succeed.
74      *
75      * @see android.content.pm.PackageItemInfo#metaData
76      */
77     @NonNull
78     private final List<RequiredMetaData> mMetaData;
79 
RequiredComponent(@onNull IntentFilterData intentFilterData, int minTargetSdkVersion, @Nullable String permission, int queryFlags, @NonNull List<RequiredMetaData> metaData)80     public RequiredComponent(@NonNull IntentFilterData intentFilterData, int minTargetSdkVersion,
81             @Nullable String permission, int queryFlags, @NonNull List<RequiredMetaData> metaData) {
82         mIntentFilterData = intentFilterData;
83         mMinTargetSdkVersion = minTargetSdkVersion;
84         mPermission = permission;
85         mQueryFlags = queryFlags;
86         mMetaData = metaData;
87     }
88 
89     @NonNull
getIntentFilterData()90     public IntentFilterData getIntentFilterData() {
91         return mIntentFilterData;
92     }
93 
getMinTargetSdkVersion()94     public int getMinTargetSdkVersion() {
95         return mMinTargetSdkVersion;
96     }
97 
98     /**
99      * Check whether this required component is available.
100      *
101      * @return whether this required component is available
102      */
isAvailable()103     public boolean isAvailable() {
104         // Workaround to match the value 33+ for T+ in roles.xml before SDK finalization.
105         if (mMinTargetSdkVersion >= 33) {
106             return SdkLevel.isAtLeastT();
107         } else {
108             return Build.VERSION.SDK_INT >= mMinTargetSdkVersion;
109         }
110     }
111 
112     /**
113      * Check whether this required component is required for a package.
114      *
115      * @param applicationInfo the {@link ApplicationInfo} for the package
116      * @return whether this required component is required
117      */
isRequired(@onNull ApplicationInfo applicationInfo)118     public boolean isRequired(@NonNull ApplicationInfo applicationInfo) {
119         return isAvailable() && applicationInfo.targetSdkVersion >= mMinTargetSdkVersion;
120     }
121 
122     @Nullable
getPermission()123     public String getPermission() {
124         return mPermission;
125     }
126 
127     @NonNull
getMetaData()128     public List<RequiredMetaData> getMetaData() {
129         return mMetaData;
130     }
131 
132     /**
133      * Get the component that matches this required component within a package, if any.
134      *
135      * @param packageName the package name for this query
136      * @param context the {@code Context} to retrieve system services
137      *
138      * @return the matching component, or {@code null} if none.
139      */
140     @Nullable
getQualifyingComponentForPackage(@onNull String packageName, @NonNull Context context)141     public ComponentName getQualifyingComponentForPackage(@NonNull String packageName,
142             @NonNull Context context) {
143         List<ComponentName> componentNames = getQualifyingComponentsInternal(packageName,
144                 Process.myUserHandle(), context);
145         return !componentNames.isEmpty() ? componentNames.get(0) : null;
146     }
147 
148     /**
149      * Get the list of components that match this required component, <b>at most one component per
150      * package</b> and ordered from best to worst.
151      *
152      * @param user the user to get the qualifying components.
153      * @param context the {@code Context} to retrieve system services
154      *
155      * @return the list of matching components
156      *
157      * @see Role#getQualifyingPackagesAsUser(UserHandle, Context)
158      */
159     @NonNull
getQualifyingComponentsAsUser(@onNull UserHandle user, @NonNull Context context)160     public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user,
161             @NonNull Context context) {
162         return getQualifyingComponentsInternal(null, user, context);
163     }
164 
165     @NonNull
getQualifyingComponentsInternal(@ullable String packageName, @NonNull UserHandle user, @NonNull Context context)166     private List<ComponentName> getQualifyingComponentsInternal(@Nullable String packageName,
167             @NonNull UserHandle user, @NonNull Context context) {
168         Intent intent = mIntentFilterData.createIntent();
169         if (packageName != null) {
170             intent.setPackage(packageName);
171         }
172         int flags = mQueryFlags | PackageManager.MATCH_DIRECT_BOOT_AWARE
173                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
174         boolean hasMetaData = !mMetaData.isEmpty();
175         if (hasMetaData) {
176             flags |= PackageManager.GET_META_DATA;
177         }
178         List<ResolveInfo> resolveInfos = queryIntentComponentsAsUser(intent, flags, user, context);
179 
180         ArraySet<String> componentPackageNames = new ArraySet<>();
181         List<ComponentName> componentNames = new ArrayList<>();
182         int resolveInfosSize = resolveInfos.size();
183         for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) {
184             ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex);
185 
186             if (mPermission != null) {
187                 String componentPermission = getComponentPermission(resolveInfo);
188                 if (!Objects.equals(componentPermission, mPermission)) {
189                     continue;
190                 }
191             }
192 
193             if (hasMetaData) {
194                 Bundle componentMetaData = getComponentMetaData(resolveInfo);
195                 if (componentMetaData == null) {
196                     componentMetaData = Bundle.EMPTY;
197                 }
198                 boolean isMetaDataQualified = true;
199                 int metaDataSize = mMetaData.size();
200                 for (int metaDataIndex = 0; metaDataIndex < metaDataSize; metaDataIndex++) {
201                     RequiredMetaData metaData = mMetaData.get(metaDataIndex);
202 
203                     if (!metaData.isQualified(componentMetaData)) {
204                         isMetaDataQualified = false;
205                         break;
206                     }
207                 }
208                 if (!isMetaDataQualified) {
209                     continue;
210                 }
211             }
212 
213             ComponentName componentName = getComponentComponentName(resolveInfo);
214             String componentPackageName = componentName.getPackageName();
215             if (componentPackageNames.contains(componentPackageName)) {
216                 continue;
217             }
218 
219             componentPackageNames.add(componentPackageName);
220             componentNames.add(componentName);
221         }
222         return componentNames;
223     }
224 
225     /**
226      * Query the {@code PackageManager} for components matching an {@code Intent}, ordered from best
227      * to worst.
228      *
229      * @param intent the {@code Intent} to match against
230      * @param flags the flags for this query
231      * @param user the user for this query
232      * @param context the {@code Context} to retrieve system services
233      *
234      * @return the list of matching components
235      */
236     @NonNull
queryIntentComponentsAsUser(@onNull Intent intent, int flags, @NonNull UserHandle user, @NonNull Context context)237     protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent,
238             int flags, @NonNull UserHandle user, @NonNull Context context);
239 
240     /**
241      * Get the {@code ComponentName} of a component.
242      *
243      * @param resolveInfo the {@code ResolveInfo} of the component
244      *
245      * @return the {@code ComponentName} of the component
246      */
247     @NonNull
getComponentComponentName(@onNull ResolveInfo resolveInfo)248     protected abstract ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo);
249 
250     /**
251      * Get the permission required to access a component.
252      *
253      * @param resolveInfo the {@code ResolveInfo} of the component
254      *
255      * @return the permission required to access a component
256      */
257     @Nullable
getComponentPermission(@onNull ResolveInfo resolveInfo)258     protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo);
259 
260     /**
261      * Get the meta data associated with a component.
262      *
263      * @param resolveInfo the {@code ResolveInfo} of the component
264      *
265      * @return the meta data associated with a component
266      */
267     @Nullable
getComponentMetaData(@onNull ResolveInfo resolveInfo)268     protected abstract Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo);
269 
270     @Override
toString()271     public String toString() {
272         return "RequiredComponent{"
273                 + "mIntentFilterData=" + mIntentFilterData
274                 + ", mMinTargetSdkVersion=" + mMinTargetSdkVersion
275                 + ", mPermission='" + mPermission + '\''
276                 + ", mQueryFlags=" + mQueryFlags
277                 + ", mMetaData=" + mMetaData
278                 + '}';
279     }
280 
281     @Override
equals(Object object)282     public boolean equals(Object object) {
283         if (this == object) {
284             return true;
285         }
286         if (object == null || getClass() != object.getClass()) {
287             return false;
288         }
289         RequiredComponent that = (RequiredComponent) object;
290         return Objects.equals(mIntentFilterData, that.mIntentFilterData)
291                 && Objects.equals(mMinTargetSdkVersion, that.mMinTargetSdkVersion)
292                 && Objects.equals(mPermission, that.mPermission)
293                 && mQueryFlags == that.mQueryFlags
294                 && Objects.equals(mMetaData, that.mMetaData);
295     }
296 
297     @Override
hashCode()298     public int hashCode() {
299         return Objects.hash(mIntentFilterData, mMinTargetSdkVersion, mPermission, mQueryFlags,
300                 mMetaData);
301     }
302 }
303