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