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.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.os.Build; 26 import android.os.Bundle; 27 import android.os.Process; 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 34+ for U+ in roles.xml before SDK finalization. 112 if (mMinTargetSdkVersion >= 34) { 113 return SdkLevel.isAtLeastU(); 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 context the {@code Context} to retrieve system services 148 * 149 * @return the matching component, or {@code null} if none. 150 */ 151 @Nullable getQualifyingComponentForPackage(@onNull String packageName, @NonNull Context context)152 public ComponentName getQualifyingComponentForPackage(@NonNull String packageName, 153 @NonNull Context context) { 154 List<ComponentName> componentNames = getQualifyingComponentsInternal(packageName, 155 Process.myUserHandle(), context); 156 return !componentNames.isEmpty() ? componentNames.get(0) : null; 157 } 158 159 /** 160 * Get the list of components that match this required component, <b>at most one component per 161 * package</b> and ordered from best to worst. 162 * 163 * @param user the user to get the qualifying components. 164 * @param context the {@code Context} to retrieve system services 165 * 166 * @return the list of matching components 167 * 168 * @see Role#getQualifyingPackagesAsUser(UserHandle, Context) 169 */ 170 @NonNull getQualifyingComponentsAsUser(@onNull UserHandle user, @NonNull Context context)171 public List<ComponentName> getQualifyingComponentsAsUser(@NonNull UserHandle user, 172 @NonNull Context context) { 173 return getQualifyingComponentsInternal(null, user, context); 174 } 175 176 @NonNull getQualifyingComponentsInternal(@ullable String packageName, @NonNull UserHandle user, @NonNull Context context)177 private List<ComponentName> getQualifyingComponentsInternal(@Nullable String packageName, 178 @NonNull UserHandle user, @NonNull Context context) { 179 Intent intent = mIntentFilterData.createIntent(); 180 if (packageName != null) { 181 intent.setPackage(packageName); 182 } 183 int queryFlags = mQueryFlags | PackageManager.MATCH_DIRECT_BOOT_AWARE 184 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 185 boolean hasMetaData = !mMetaData.isEmpty(); 186 if (hasMetaData) { 187 queryFlags |= PackageManager.GET_META_DATA; 188 } 189 List<ResolveInfo> resolveInfos = queryIntentComponentsAsUser(intent, queryFlags, user, 190 context); 191 192 ArraySet<String> componentPackageNames = new ArraySet<>(); 193 List<ComponentName> componentNames = new ArrayList<>(); 194 int resolveInfosSize = resolveInfos.size(); 195 for (int resolveInfosIndex = 0; resolveInfosIndex < resolveInfosSize; resolveInfosIndex++) { 196 ResolveInfo resolveInfo = resolveInfos.get(resolveInfosIndex); 197 198 if (mFlags != 0) { 199 int componentFlags = getComponentFlags(resolveInfo); 200 if ((componentFlags & mFlags) != mFlags) { 201 continue; 202 } 203 } 204 205 if (mPermission != null) { 206 String componentPermission = getComponentPermission(resolveInfo); 207 if (!Objects.equals(componentPermission, mPermission)) { 208 continue; 209 } 210 } 211 212 if (hasMetaData) { 213 Bundle componentMetaData = getComponentMetaData(resolveInfo); 214 if (componentMetaData == null) { 215 componentMetaData = Bundle.EMPTY; 216 } 217 boolean isMetaDataQualified = true; 218 int metaDataSize = mMetaData.size(); 219 for (int metaDataIndex = 0; metaDataIndex < metaDataSize; metaDataIndex++) { 220 RequiredMetaData metaData = mMetaData.get(metaDataIndex); 221 222 if (!metaData.isQualified(componentMetaData)) { 223 isMetaDataQualified = false; 224 break; 225 } 226 } 227 if (!isMetaDataQualified) { 228 continue; 229 } 230 } 231 232 ComponentName componentName = getComponentComponentName(resolveInfo); 233 String componentPackageName = componentName.getPackageName(); 234 if (componentPackageNames.contains(componentPackageName)) { 235 continue; 236 } 237 238 componentPackageNames.add(componentPackageName); 239 componentNames.add(componentName); 240 } 241 return componentNames; 242 } 243 244 /** 245 * Query the {@code PackageManager} for components matching an {@code Intent}, ordered from best 246 * to worst. 247 * 248 * @param intent the {@code Intent} to match against 249 * @param flags the flags for this query 250 * @param user the user for this query 251 * @param context the {@code Context} to retrieve system services 252 * 253 * @return the list of matching components 254 */ 255 @NonNull queryIntentComponentsAsUser(@onNull Intent intent, int flags, @NonNull UserHandle user, @NonNull Context context)256 protected abstract List<ResolveInfo> queryIntentComponentsAsUser(@NonNull Intent intent, 257 int flags, @NonNull UserHandle user, @NonNull Context context); 258 259 /** 260 * Get the {@code ComponentName} of a component. 261 * 262 * @param resolveInfo the {@code ResolveInfo} of the component 263 * 264 * @return the {@code ComponentName} of the component 265 */ 266 @NonNull getComponentComponentName(@onNull ResolveInfo resolveInfo)267 protected abstract ComponentName getComponentComponentName(@NonNull ResolveInfo resolveInfo); 268 269 /** 270 * Get the flags that have been set on a component. 271 * 272 * @param resolveInfo the {@code ResolveInfo} of the component 273 * 274 * @return the flags that have been set on a component 275 */ getComponentFlags(@onNull ResolveInfo resolveInfo)276 protected abstract int getComponentFlags(@NonNull ResolveInfo resolveInfo); 277 278 /** 279 * Get the permission required to access a component. 280 * 281 * @param resolveInfo the {@code ResolveInfo} of the component 282 * 283 * @return the permission required to access a component 284 */ 285 @Nullable getComponentPermission(@onNull ResolveInfo resolveInfo)286 protected abstract String getComponentPermission(@NonNull ResolveInfo resolveInfo); 287 288 /** 289 * Get the meta data associated with a component. 290 * 291 * @param resolveInfo the {@code ResolveInfo} of the component 292 * 293 * @return the meta data associated with a component 294 */ 295 @Nullable getComponentMetaData(@onNull ResolveInfo resolveInfo)296 protected abstract Bundle getComponentMetaData(@NonNull ResolveInfo resolveInfo); 297 298 @Override toString()299 public String toString() { 300 return "RequiredComponent{" 301 + "mIntentFilterData=" + mIntentFilterData 302 + ", mMinTargetSdkVersion=" + mMinTargetSdkVersion 303 + ", mFlags='" + mFlags + '\'' 304 + ", mPermission='" + mPermission + '\'' 305 + ", mQueryFlags=" + mQueryFlags 306 + ", mMetaData=" + mMetaData 307 + '}'; 308 } 309 310 @Override equals(Object object)311 public boolean equals(Object object) { 312 if (this == object) { 313 return true; 314 } 315 if (object == null || getClass() != object.getClass()) { 316 return false; 317 } 318 RequiredComponent that = (RequiredComponent) object; 319 return Objects.equals(mIntentFilterData, that.mIntentFilterData) 320 && Objects.equals(mMinTargetSdkVersion, that.mMinTargetSdkVersion) 321 && mFlags == that.mFlags 322 && Objects.equals(mPermission, that.mPermission) 323 && mQueryFlags == that.mQueryFlags 324 && Objects.equals(mMetaData, that.mMetaData); 325 } 326 327 @Override hashCode()328 public int hashCode() { 329 return Objects.hash(mIntentFilterData, mMinTargetSdkVersion, mFlags, mPermission, 330 mQueryFlags, mMetaData); 331 } 332 } 333