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.role.controller.model; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.ComponentInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ResolveInfo; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.util.ArraySet; 30 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 34 import com.android.modules.utils.build.SdkLevel; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Specifies a required component for an application to qualify for a {@link Role}. 42 */ 43 public abstract class RequiredComponent { 44 45 /** 46 * The {@code Intent} or {@code IntentFilter} data to match the components. 47 */ 48 @NonNull 49 private final IntentFilterData mIntentFilterData; 50 51 /** 52 * The minimum target SDK version for this component to be required. 53 * <p> 54 * This also implies a minimum platform SDK version for this component to be required. 55 */ 56 private final int mMinTargetSdkVersion; 57 58 /** 59 * Optional flags required to be set on a component for match to succeed. 60 */ 61 private final int mFlags; 62 63 /** 64 * Optional permission required on a component for match to succeed. 65 * 66 * @see android.content.pm.ActivityInfo#permission 67 * @see android.content.pm.ServiceInfo#permission 68 */ 69 @Nullable 70 private final String mPermission; 71 72 /** 73 * The query flags to match the components with. 74 */ 75 private final int mQueryFlags; 76 77 /** 78 * The meta data required on a component for match to succeed. 79 * 80 * @see android.content.pm.PackageItemInfo#metaData 81 */ 82 @NonNull 83 private final List<RequiredMetaData> mMetaData; 84 RequiredComponent(@onNull IntentFilterData intentFilterData, int minTargetSdkVersion, int flags, @Nullable String permission, int queryFlags, @NonNull List<RequiredMetaData> metaData)85 public RequiredComponent(@NonNull IntentFilterData intentFilterData, int minTargetSdkVersion, 86 int flags, @Nullable String permission, int queryFlags, 87 @NonNull List<RequiredMetaData> metaData) { 88 mIntentFilterData = intentFilterData; 89 mMinTargetSdkVersion = minTargetSdkVersion; 90 mFlags = flags; 91 mPermission = permission; 92 mQueryFlags = queryFlags; 93 mMetaData = metaData; 94 } 95 96 @NonNull getIntentFilterData()97 public IntentFilterData getIntentFilterData() { 98 return mIntentFilterData; 99 } 100 getMinTargetSdkVersion()101 public int getMinTargetSdkVersion() { 102 return mMinTargetSdkVersion; 103 } 104 105 /** 106 * Check whether this required component is available. 107 * 108 * @return whether this required component is available 109 */ isAvailable()110 public boolean isAvailable() { 111 // Workaround to match the value 35+ for V+ in roles.xml before SDK finalization. 112 if (mMinTargetSdkVersion >= 35) { 113 return SdkLevel.isAtLeastV(); 114 } else { 115 return Build.VERSION.SDK_INT >= mMinTargetSdkVersion; 116 } 117 } 118 119 /** 120 * Check whether this required component is required for a package. 121 * 122 * @param applicationInfo the {@link ApplicationInfo} for the package 123 * @return whether this required component is required 124 */ isRequired(@onNull ApplicationInfo applicationInfo)125 public boolean isRequired(@NonNull ApplicationInfo applicationInfo) { 126 return isAvailable() && applicationInfo.targetSdkVersion >= mMinTargetSdkVersion; 127 } 128 getFlags()129 public int getFlags() { 130 return mFlags; 131 } 132 133 @Nullable getPermission()134 public String getPermission() { 135 return mPermission; 136 } 137 138 @NonNull getMetaData()139 public List<RequiredMetaData> getMetaData() { 140 return mMetaData; 141 } 142 143 /** 144 * Get the component that matches this required component within a package, if any. 145 * 146 * @param packageName the package name for this query 147 * @param user the user of the component 148 * @param context the {@code Context} to retrieve system services 149 * 150 * @return the matching component, or {@code null} if none. 151 */ 152 @Nullable getQualifyingComponentForPackageAsUser(@onNull String packageName, @NonNull UserHandle user, @NonNull Context context)153 public ComponentName getQualifyingComponentForPackageAsUser(@NonNull String packageName, 154 @NonNull UserHandle user, @NonNull Context context) { 155 List<ComponentName> componentNames = getQualifyingComponentsAsUserInternal(packageName, 156 user, context); 157 return !componentNames.isEmpty() ? componentNames.get(0) : null; 158 } 159 160 /** 161 * Get the list of components that match this required component, <b>at most one component per 162 * package</b> and ordered from best to worst. 163 * 164 * @param user the user to get the qualifying components. 165 * @param context the {@code Context} to retrieve system services 166 * 167 * @return the list of matching components 168 * 169 * @see Role#getQualifyingPackagesAsUser(UserHandle, Context) 170 */ 171 @NonNull getQualifyingComponentsAsUser(@onNull UserHandle user, @NonNull Context context)172 public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user, 173 @NonNull Context context) { 174 return getQualifyingComponentsAsUserInternal(null, user, context); 175 } 176 177 @NonNull getQualifyingComponentsAsUserInternal(@ullable String packageName, @NonNull UserHandle user, @NonNull Context context)178 private List<ComponentName> getQualifyingComponentsAsUserInternal(@Nullable String packageName, 179 @NonNull UserHandle user, @NonNull Context context) { 180 Intent intent = mIntentFilterData.createIntent(); 181 if (packageName != null) { 182 intent.setPackage(packageName); 183 } 184 int queryFlags = mQueryFlags | PackageManager.MATCH_DIRECT_BOOT_AWARE 185 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 186 boolean hasMetaData = !mMetaData.isEmpty(); 187 if (hasMetaData) { 188 queryFlags |= PackageManager.GET_META_DATA; 189 } 190 List<ResolveInfo> resolveInfos = queryIntentComponentsAsUser(intent, queryFlags, user, 191 context); 192 193 ArraySet<String> componentPackageNames = new ArraySet<>(); 194 List<ComponentName> componentNames = new ArrayList<>(); 195 int resolveInfosSize = resolveInfos.size(); 196 for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) { 197 ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex); 198 199 if (!isComponentQualified(resolveInfo)) { 200 continue; 201 } 202 203 if (mFlags != 0) { 204 int componentFlags = getComponentFlags(resolveInfo); 205 if ((componentFlags & mFlags) != mFlags) { 206 continue; 207 } 208 } 209 210 if (mPermission != null) { 211 String componentPermission = getComponentPermission(resolveInfo); 212 if (!Objects.equals(componentPermission, mPermission)) { 213 continue; 214 } 215 } 216 217 ComponentInfo componentInfo = getComponentComponentInfo(resolveInfo); 218 if (hasMetaData) { 219 Bundle componentMetaData = componentInfo.metaData; 220 if (componentMetaData == null) { 221 componentMetaData = Bundle.EMPTY; 222 } 223 boolean isMetaDataQualified = true; 224 int metaDataSize = mMetaData.size(); 225 for (int metaDataIndex = 0; metaDataIndex < metaDataSize; metaDataIndex++) { 226 RequiredMetaData metaData = mMetaData.get(metaDataIndex); 227 228 if (!metaData.isQualified(componentMetaData)) { 229 isMetaDataQualified = false; 230 break; 231 } 232 } 233 if (!isMetaDataQualified) { 234 continue; 235 } 236 } 237 238 String componentPackageName = componentInfo.packageName; 239 if (componentPackageNames.contains(componentPackageName)) { 240 continue; 241 } 242 componentPackageNames.add(componentPackageName); 243 244 ComponentName componentName = new ComponentName(componentPackageName, 245 componentInfo.name); 246 componentNames.add(componentName); 247 } 248 return componentNames; 249 } 250 251 /** 252 * Query the {@code PackageManager} for components matching an {@code Intent}, ordered from best 253 * to worst. 254 * 255 * @param intent the {@code Intent} to match against 256 * @param flags the flags for this query 257 * @param user the user for this query 258 * @param context the {@code Context} to retrieve system services 259 * 260 * @return the list of matching components 261 */ 262 @NonNull queryIntentComponentsAsUser(@onNull Intent intent, int flags, @NonNull UserHandle user, @NonNull Context context)263 protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent, 264 int flags, @NonNull UserHandle user, @NonNull Context context); 265 isComponentQualified(@onNull ResolveInfo resolveInfo)266 protected boolean isComponentQualified(@NonNull ResolveInfo resolveInfo) { 267 return true; 268 } 269 270 /** 271 * Get the {@code ComponentInfo} of a component. 272 * 273 * @param resolveInfo the {@code ResolveInfo} of the component 274 * 275 * @return the {@code ComponentInfo} of the component 276 */ 277 @NonNull getComponentComponentInfo(@onNull ResolveInfo resolveInfo)278 protected abstract ComponentInfo getComponentComponentInfo(@NonNull ResolveInfo resolveInfo); 279 280 /** 281 * Get the flags that have been set on a component. 282 * 283 * @param resolveInfo the {@code ResolveInfo} of the component 284 * 285 * @return the flags that have been set on a component 286 */ getComponentFlags(@onNull ResolveInfo resolveInfo)287 protected abstract int getComponentFlags(@NonNull ResolveInfo resolveInfo); 288 289 /** 290 * Get the permission required to access a component. 291 * 292 * @param resolveInfo the {@code ResolveInfo} of the component 293 * 294 * @return the permission required to access a component 295 */ 296 @Nullable getComponentPermission(@onNull ResolveInfo resolveInfo)297 protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo); 298 299 @Override toString()300 public String toString() { 301 return "RequiredComponent{" 302 + "mIntentFilterData=" + mIntentFilterData 303 + ", mMinTargetSdkVersion=" + mMinTargetSdkVersion 304 + ", mFlags='" + mFlags + '\'' 305 + ", mPermission='" + mPermission + '\'' 306 + ", mQueryFlags=" + mQueryFlags 307 + ", mMetaData=" + mMetaData 308 + '}'; 309 } 310 311 @Override equals(Object object)312 public boolean equals(Object object) { 313 if (this == object) { 314 return true; 315 } 316 if (object == null || getClass() != object.getClass()) { 317 return false; 318 } 319 RequiredComponent that = (RequiredComponent) object; 320 return Objects.equals(mIntentFilterData, that.mIntentFilterData) 321 && Objects.equals(mMinTargetSdkVersion, that.mMinTargetSdkVersion) 322 && mFlags == that.mFlags 323 && Objects.equals(mPermission, that.mPermission) 324 && mQueryFlags == that.mQueryFlags 325 && Objects.equals(mMetaData, that.mMetaData); 326 } 327 328 @Override hashCode()329 public int hashCode() { 330 return Objects.hash(mIntentFilterData, mMinTargetSdkVersion, mFlags, mPermission, 331 mQueryFlags, mMetaData); 332 } 333 } 334