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