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