• 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.role.controller.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 flags required to be set on a component for match to succeed.
60      */
61     private final int mFlags;
62 
63     /**
64      * Optional permission required on a component for match to succeed.
65      *
66      * @see android.content.pm.ActivityInfo#permission
67      * @see android.content.pm.ServiceInfo#permission
68      */
69     @Nullable
70     private final String mPermission;
71 
72     /**
73      * The query flags to match the components with.
74      */
75     private final int mQueryFlags;
76 
77     /**
78      * The meta data required on a component for match to succeed.
79      *
80      * @see android.content.pm.PackageItemInfo#metaData
81      */
82     @NonNull
83     private final List<RequiredMetaData> mMetaData;
84 
RequiredComponent(@onNull IntentFilterData intentFilterData, int minTargetSdkVersion, int flags, @Nullable String permission, int queryFlags, @NonNull List<RequiredMetaData> metaData)85     public RequiredComponent(@NonNull IntentFilterData intentFilterData, int minTargetSdkVersion,
86             int flags, @Nullable String permission, int queryFlags,
87             @NonNull List<RequiredMetaData> metaData) {
88         mIntentFilterData = intentFilterData;
89         mMinTargetSdkVersion = minTargetSdkVersion;
90         mFlags = flags;
91         mPermission = permission;
92         mQueryFlags = queryFlags;
93         mMetaData = metaData;
94     }
95 
96     @NonNull
getIntentFilterData()97     public IntentFilterData getIntentFilterData() {
98         return mIntentFilterData;
99     }
100 
getMinTargetSdkVersion()101     public int getMinTargetSdkVersion() {
102         return mMinTargetSdkVersion;
103     }
104 
105     /**
106      * Check whether this required component is available.
107      *
108      * @return whether this required component is available
109      */
isAvailable()110     public boolean isAvailable() {
111         // Workaround to match the value 34+ for U+ in roles.xml before SDK finalization.
112         if (mMinTargetSdkVersion >= 34) {
113             return SdkLevel.isAtLeastU();
114         } else {
115             return Build.VERSION.SDK_INT >= mMinTargetSdkVersion;
116         }
117     }
118 
119     /**
120      * Check whether this required component is required for a package.
121      *
122      * @param applicationInfo the {@link ApplicationInfo} for the package
123      * @return whether this required component is required
124      */
isRequired(@onNull ApplicationInfo applicationInfo)125     public boolean isRequired(@NonNull ApplicationInfo applicationInfo) {
126         return isAvailable() && applicationInfo.targetSdkVersion >= mMinTargetSdkVersion;
127     }
128 
getFlags()129     public int getFlags() {
130         return mFlags;
131     }
132 
133     @Nullable
getPermission()134     public String getPermission() {
135         return mPermission;
136     }
137 
138     @NonNull
getMetaData()139     public List<RequiredMetaData> getMetaData() {
140         return mMetaData;
141     }
142 
143     /**
144      * Get the component that matches this required component within a package, if any.
145      *
146      * @param packageName the package name for this query
147      * @param context the {@code Context} to retrieve system services
148      *
149      * @return the matching component, or {@code null} if none.
150      */
151     @Nullable
getQualifyingComponentForPackage(@onNull String packageName, @NonNull Context context)152     public ComponentName getQualifyingComponentForPackage(@NonNull String packageName,
153             @NonNull Context context) {
154         List<ComponentName> componentNames = getQualifyingComponentsInternal(packageName,
155                 Process.myUserHandle(), context);
156         return !componentNames.isEmpty() ? componentNames.get(0) : null;
157     }
158 
159     /**
160      * Get the list of components that match this required component, <b>at most one component per
161      * package</b> and ordered from best to worst.
162      *
163      * @param user the user to get the qualifying components.
164      * @param context the {@code Context} to retrieve system services
165      *
166      * @return the list of matching components
167      *
168      * @see Role#getQualifyingPackagesAsUser(UserHandle, Context)
169      */
170     @NonNull
getQualifyingComponentsAsUser(@onNull UserHandle user, @NonNull Context context)171     public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user,
172             @NonNull Context context) {
173         return getQualifyingComponentsInternal(null, user, context);
174     }
175 
176     @NonNull
getQualifyingComponentsInternal(@ullable String packageName, @NonNull UserHandle user, @NonNull Context context)177     private List<ComponentName> getQualifyingComponentsInternal(@Nullable String packageName,
178             @NonNull UserHandle user, @NonNull Context context) {
179         Intent intent = mIntentFilterData.createIntent();
180         if (packageName != null) {
181             intent.setPackage(packageName);
182         }
183         int queryFlags = mQueryFlags | PackageManager.MATCH_DIRECT_BOOT_AWARE
184                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
185         boolean hasMetaData = !mMetaData.isEmpty();
186         if (hasMetaData) {
187             queryFlags |= PackageManager.GET_META_DATA;
188         }
189         List<ResolveInfo> resolveInfos = queryIntentComponentsAsUser(intent, queryFlags, user,
190                 context);
191 
192         ArraySet<String> componentPackageNames = new ArraySet<>();
193         List<ComponentName> componentNames = new ArrayList<>();
194         int resolveInfosSize = resolveInfos.size();
195         for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) {
196             ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex);
197 
198             if (mFlags != 0) {
199                 int componentFlags = getComponentFlags(resolveInfo);
200                 if ((componentFlags & mFlags) != mFlags) {
201                     continue;
202                 }
203             }
204 
205             if (mPermission != null) {
206                 String componentPermission = getComponentPermission(resolveInfo);
207                 if (!Objects.equals(componentPermission, mPermission)) {
208                     continue;
209                 }
210             }
211 
212             if (hasMetaData) {
213                 Bundle componentMetaData = getComponentMetaData(resolveInfo);
214                 if (componentMetaData == null) {
215                     componentMetaData = Bundle.EMPTY;
216                 }
217                 boolean isMetaDataQualified = true;
218                 int metaDataSize = mMetaData.size();
219                 for (int metaDataIndex = 0; metaDataIndex < metaDataSize; metaDataIndex++) {
220                     RequiredMetaData metaData = mMetaData.get(metaDataIndex);
221 
222                     if (!metaData.isQualified(componentMetaData)) {
223                         isMetaDataQualified = false;
224                         break;
225                     }
226                 }
227                 if (!isMetaDataQualified) {
228                     continue;
229                 }
230             }
231 
232             ComponentName componentName = getComponentComponentName(resolveInfo);
233             String componentPackageName = componentName.getPackageName();
234             if (componentPackageNames.contains(componentPackageName)) {
235                 continue;
236             }
237 
238             componentPackageNames.add(componentPackageName);
239             componentNames.add(componentName);
240         }
241         return componentNames;
242     }
243 
244     /**
245      * Query the {@code PackageManager} for components matching an {@code Intent}, ordered from best
246      * to worst.
247      *
248      * @param intent the {@code Intent} to match against
249      * @param flags the flags for this query
250      * @param user the user for this query
251      * @param context the {@code Context} to retrieve system services
252      *
253      * @return the list of matching components
254      */
255     @NonNull
queryIntentComponentsAsUser(@onNull Intent intent, int flags, @NonNull UserHandle user, @NonNull Context context)256     protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent,
257             int flags, @NonNull UserHandle user, @NonNull Context context);
258 
259     /**
260      * Get the {@code ComponentName} of a component.
261      *
262      * @param resolveInfo the {@code ResolveInfo} of the component
263      *
264      * @return the {@code ComponentName} of the component
265      */
266     @NonNull
getComponentComponentName(@onNull ResolveInfo resolveInfo)267     protected abstract ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo);
268 
269     /**
270      * Get the flags that have been set on a component.
271      *
272      * @param resolveInfo the {@code ResolveInfo} of the component
273      *
274      * @return the flags that have been set on a component
275      */
getComponentFlags(@onNull ResolveInfo resolveInfo)276     protected abstract int getComponentFlags(@NonNull ResolveInfo resolveInfo);
277 
278     /**
279      * Get the permission required to access a component.
280      *
281      * @param resolveInfo the {@code ResolveInfo} of the component
282      *
283      * @return the permission required to access a component
284      */
285     @Nullable
getComponentPermission(@onNull ResolveInfo resolveInfo)286     protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo);
287 
288     /**
289      * Get the meta data associated with a component.
290      *
291      * @param resolveInfo the {@code ResolveInfo} of the component
292      *
293      * @return the meta data associated with a component
294      */
295     @Nullable
getComponentMetaData(@onNull ResolveInfo resolveInfo)296     protected abstract Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo);
297 
298     @Override
toString()299     public String toString() {
300         return "RequiredComponent{"
301                 + "mIntentFilterData=" + mIntentFilterData
302                 + ", mMinTargetSdkVersion=" + mMinTargetSdkVersion
303                 + ", mFlags='" + mFlags + '\''
304                 + ", mPermission='" + mPermission + '\''
305                 + ", mQueryFlags=" + mQueryFlags
306                 + ", mMetaData=" + mMetaData
307                 + '}';
308     }
309 
310     @Override
equals(Object object)311     public boolean equals(Object object) {
312         if (this == object) {
313             return true;
314         }
315         if (object == null || getClass() != object.getClass()) {
316             return false;
317         }
318         RequiredComponent that = (RequiredComponent) object;
319         return Objects.equals(mIntentFilterData, that.mIntentFilterData)
320                 && Objects.equals(mMinTargetSdkVersion, that.mMinTargetSdkVersion)
321                 && mFlags == that.mFlags
322                 && Objects.equals(mPermission, that.mPermission)
323                 && mQueryFlags == that.mQueryFlags
324                 && Objects.equals(mMetaData, that.mMetaData);
325     }
326 
327     @Override
hashCode()328     public int hashCode() {
329         return Objects.hash(mIntentFilterData, mMinTargetSdkVersion, mFlags, mPermission,
330                 mQueryFlags, mMetaData);
331     }
332 }
333