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