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.ComponentInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.ResolveInfo;
26 import android.os.Build;
27 import android.os.Bundle;
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 35+ for V+ in roles.xml before SDK finalization.
112         if (mMinTargetSdkVersion >= 35) {
113             return SdkLevel.isAtLeastV();
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 user the user of the component
148      * @param context the {@code Context} to retrieve system services
149      *
150      * @return the matching component, or {@code null} if none.
151      */
152     @Nullable
getQualifyingComponentForPackageAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)153     public ComponentName getQualifyingComponentForPackageAsUser(@NonNull String packageName,
154             @NonNull UserHandle user, @NonNull Context context) {
155         List<ComponentName> componentNames = getQualifyingComponentsAsUserInternal(packageName,
156                 user, context);
157         return !componentNames.isEmpty() ? componentNames.get(0) : null;
158     }
159 
160     /**
161      * Get the list of components that match this required component, <b>at most one component per
162      * package</b> and ordered from best to worst.
163      *
164      * @param user the user to get the qualifying components.
165      * @param context the {@code Context} to retrieve system services
166      *
167      * @return the list of matching components
168      *
169      * @see Role#getQualifyingPackagesAsUser(UserHandle, Context)
170      */
171     @NonNull
getQualifyingComponentsAsUser(@onNull UserHandle user, @NonNull Context context)172     public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user,
173             @NonNull Context context) {
174         return getQualifyingComponentsAsUserInternal(null, user, context);
175     }
176 
177     @NonNull
getQualifyingComponentsAsUserInternal(@ullable String packageName, @NonNull UserHandle user, @NonNull Context context)178     private List<ComponentName> getQualifyingComponentsAsUserInternal(@Nullable String packageName,
179             @NonNull UserHandle user, @NonNull Context context) {
180         Intent intent = mIntentFilterData.createIntent();
181         if (packageName != null) {
182             intent.setPackage(packageName);
183         }
184         int queryFlags = mQueryFlags | PackageManager.MATCH_DIRECT_BOOT_AWARE
185                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
186         boolean hasMetaData = !mMetaData.isEmpty();
187         if (hasMetaData) {
188             queryFlags |= PackageManager.GET_META_DATA;
189         }
190         List<ResolveInfo> resolveInfos = queryIntentComponentsAsUser(intent, queryFlags, user,
191                 context);
192 
193         ArraySet<String> componentPackageNames = new ArraySet<>();
194         List<ComponentName> componentNames = new ArrayList<>();
195         int resolveInfosSize = resolveInfos.size();
196         for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) {
197             ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex);
198 
199             if (!isComponentQualified(resolveInfo)) {
200                 continue;
201             }
202 
203             if (mFlags != 0) {
204                 int componentFlags = getComponentFlags(resolveInfo);
205                 if ((componentFlags & mFlags) != mFlags) {
206                     continue;
207                 }
208             }
209 
210             if (mPermission != null) {
211                 String componentPermission = getComponentPermission(resolveInfo);
212                 if (!Objects.equals(componentPermission, mPermission)) {
213                     continue;
214                 }
215             }
216 
217             ComponentInfo componentInfo = getComponentComponentInfo(resolveInfo);
218             if (hasMetaData) {
219                 Bundle componentMetaData = componentInfo.metaData;
220                 if (componentMetaData == null) {
221                     componentMetaData = Bundle.EMPTY;
222                 }
223                 boolean isMetaDataQualified = true;
224                 int metaDataSize = mMetaData.size();
225                 for (int metaDataIndex = 0; metaDataIndex < metaDataSize; metaDataIndex++) {
226                     RequiredMetaData metaData = mMetaData.get(metaDataIndex);
227 
228                     if (!metaData.isQualified(componentMetaData)) {
229                         isMetaDataQualified = false;
230                         break;
231                     }
232                 }
233                 if (!isMetaDataQualified) {
234                     continue;
235                 }
236             }
237 
238             String componentPackageName = componentInfo.packageName;
239             if (componentPackageNames.contains(componentPackageName)) {
240                 continue;
241             }
242             componentPackageNames.add(componentPackageName);
243 
244             ComponentName componentName = new ComponentName(componentPackageName,
245                     componentInfo.name);
246             componentNames.add(componentName);
247         }
248         return componentNames;
249     }
250 
251     /**
252      * Query the {@code PackageManager} for components matching an {@code Intent}, ordered from best
253      * to worst.
254      *
255      * @param intent the {@code Intent} to match against
256      * @param flags the flags for this query
257      * @param user the user for this query
258      * @param context the {@code Context} to retrieve system services
259      *
260      * @return the list of matching components
261      */
262     @NonNull
queryIntentComponentsAsUser(@onNull Intent intent, int flags, @NonNull UserHandle user, @NonNull Context context)263     protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent,
264             int flags, @NonNull UserHandle user, @NonNull Context context);
265 
isComponentQualified(@onNull ResolveInfo resolveInfo)266     protected boolean isComponentQualified(@NonNull ResolveInfo resolveInfo) {
267         return true;
268     }
269 
270     /**
271      * Get the {@code ComponentInfo} of a component.
272      *
273      * @param resolveInfo the {@code ResolveInfo} of the component
274      *
275      * @return the {@code ComponentInfo} of the component
276      */
277     @NonNull
getComponentComponentInfo(@onNull ResolveInfo resolveInfo)278     protected abstract ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo);
279 
280     /**
281      * Get the flags that have been set on a component.
282      *
283      * @param resolveInfo the {@code ResolveInfo} of the component
284      *
285      * @return the flags that have been set on a component
286      */
getComponentFlags(@onNull ResolveInfo resolveInfo)287     protected abstract int getComponentFlags(@NonNull ResolveInfo resolveInfo);
288 
289     /**
290      * Get the permission required to access a component.
291      *
292      * @param resolveInfo the {@code ResolveInfo} of the component
293      *
294      * @return the permission required to access a component
295      */
296     @Nullable
getComponentPermission(@onNull ResolveInfo resolveInfo)297     protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo);
298 
299     @Override
toString()300     public String toString() {
301         return "RequiredComponent{"
302                 + "mIntentFilterData=" + mIntentFilterData
303                 + ", mMinTargetSdkVersion=" + mMinTargetSdkVersion
304                 + ", mFlags='" + mFlags + '\''
305                 + ", mPermission='" + mPermission + '\''
306                 + ", mQueryFlags=" + mQueryFlags
307                 + ", mMetaData=" + mMetaData
308                 + '}';
309     }
310 
311     @Override
equals(Object object)312     public boolean equals(Object object) {
313         if (this == object) {
314             return true;
315         }
316         if (object == null || getClass() != object.getClass()) {
317             return false;
318         }
319         RequiredComponent that = (RequiredComponent) object;
320         return Objects.equals(mIntentFilterData, that.mIntentFilterData)
321                 && Objects.equals(mMinTargetSdkVersion, that.mMinTargetSdkVersion)
322                 && mFlags == that.mFlags
323                 && Objects.equals(mPermission, that.mPermission)
324                 && mQueryFlags == that.mQueryFlags
325                 && Objects.equals(mMetaData, that.mMetaData);
326     }
327 
328     @Override
hashCode()329     public int hashCode() {
330         return Objects.hash(mIntentFilterData, mMinTargetSdkVersion, mFlags, mPermission,
331                 mQueryFlags, mMetaData);
332     }
333 }
334