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