1 /* 2 * Copyright (C) 2020 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 android.content.pm.parsing; 18 19 import android.annotation.CheckResult; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.apex.ApexInfo; 23 import android.content.pm.ActivityInfo; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.Attribution; 26 import android.content.pm.ComponentInfo; 27 import android.content.pm.ConfigurationInfo; 28 import android.content.pm.FallbackCategoryProvider; 29 import android.content.pm.FeatureGroupInfo; 30 import android.content.pm.FeatureInfo; 31 import android.content.pm.InstrumentationInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageItemInfo; 34 import android.content.pm.PackageManager; 35 import android.content.pm.PackageParser; 36 import android.content.pm.PackageUserState; 37 import android.content.pm.PermissionGroupInfo; 38 import android.content.pm.PermissionInfo; 39 import android.content.pm.ProviderInfo; 40 import android.content.pm.SELinuxUtil; 41 import android.content.pm.ServiceInfo; 42 import android.content.pm.Signature; 43 import android.content.pm.SigningInfo; 44 import android.content.pm.overlay.OverlayPaths; 45 import android.content.pm.parsing.component.ComponentParseUtils; 46 import android.content.pm.parsing.component.ParsedActivity; 47 import android.content.pm.parsing.component.ParsedAttribution; 48 import android.content.pm.parsing.component.ParsedComponent; 49 import android.content.pm.parsing.component.ParsedInstrumentation; 50 import android.content.pm.parsing.component.ParsedMainComponent; 51 import android.content.pm.parsing.component.ParsedPermission; 52 import android.content.pm.parsing.component.ParsedPermissionGroup; 53 import android.content.pm.parsing.component.ParsedProvider; 54 import android.content.pm.parsing.component.ParsedService; 55 import android.content.pm.parsing.component.ParsedUsesPermission; 56 import android.os.Environment; 57 import android.os.UserHandle; 58 59 import com.android.internal.util.ArrayUtils; 60 61 import libcore.util.EmptyArray; 62 63 import java.io.File; 64 import java.util.Collections; 65 import java.util.List; 66 import java.util.Set; 67 68 /** @hide **/ 69 public class PackageInfoWithoutStateUtils { 70 71 public static final String SYSTEM_DATA_PATH = 72 Environment.getDataDirectoryPath() + File.separator + "system"; 73 74 @Nullable generate(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId)75 public static PackageInfo generate(ParsingPackageRead pkg, int[] gids, 76 @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, 77 Set<String> grantedPermissions, PackageUserState state, int userId) { 78 return generateWithComponents(pkg, gids, flags, firstInstallTime, lastUpdateTime, grantedPermissions, 79 state, userId, null); 80 } 81 82 @Nullable generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags)83 public static PackageInfo generate(ParsingPackageRead pkg, ApexInfo apexInfo, int flags) { 84 return generateWithComponents(pkg, EmptyArray.INT, flags, 0, 0, Collections.emptySet(), 85 new PackageUserState(), UserHandle.getCallingUserId(), apexInfo); 86 } 87 88 @Nullable generateWithComponents(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId, @Nullable ApexInfo apexInfo)89 private static PackageInfo generateWithComponents(ParsingPackageRead pkg, int[] gids, 90 @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, 91 Set<String> grantedPermissions, PackageUserState state, int userId, 92 @Nullable ApexInfo apexInfo) { 93 ApplicationInfo applicationInfo = generateApplicationInfo(pkg, flags, state, userId); 94 if (applicationInfo == null) { 95 return null; 96 } 97 PackageInfo info = generateWithoutComponents(pkg, gids, flags, firstInstallTime, 98 lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo); 99 100 if (info == null) { 101 return null; 102 } 103 104 if ((flags & PackageManager.GET_ACTIVITIES) != 0) { 105 final int N = pkg.getActivities().size(); 106 if (N > 0) { 107 int num = 0; 108 final ActivityInfo[] res = new ActivityInfo[N]; 109 for (int i = 0; i < N; i++) { 110 final ParsedActivity a = pkg.getActivities().get(i); 111 if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a, 112 flags)) { 113 if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals( 114 a.getName())) { 115 continue; 116 } 117 res[num++] = generateActivityInfo(pkg, a, flags, state, 118 applicationInfo, userId); 119 } 120 } 121 info.activities = ArrayUtils.trimToSize(res, num); 122 } 123 } 124 if ((flags & PackageManager.GET_RECEIVERS) != 0) { 125 final int size = pkg.getReceivers().size(); 126 if (size > 0) { 127 int num = 0; 128 final ActivityInfo[] res = new ActivityInfo[size]; 129 for (int i = 0; i < size; i++) { 130 final ParsedActivity a = pkg.getReceivers().get(i); 131 if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), a, 132 flags)) { 133 res[num++] = generateActivityInfo(pkg, a, flags, state, 134 applicationInfo, userId); 135 } 136 } 137 info.receivers = ArrayUtils.trimToSize(res, num); 138 } 139 } 140 if ((flags & PackageManager.GET_SERVICES) != 0) { 141 final int size = pkg.getServices().size(); 142 if (size > 0) { 143 int num = 0; 144 final ServiceInfo[] res = new ServiceInfo[size]; 145 for (int i = 0; i < size; i++) { 146 final ParsedService s = pkg.getServices().get(i); 147 if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), s, 148 flags)) { 149 res[num++] = generateServiceInfo(pkg, s, flags, state, 150 applicationInfo, userId); 151 } 152 } 153 info.services = ArrayUtils.trimToSize(res, num); 154 } 155 } 156 if ((flags & PackageManager.GET_PROVIDERS) != 0) { 157 final int size = pkg.getProviders().size(); 158 if (size > 0) { 159 int num = 0; 160 final ProviderInfo[] res = new ProviderInfo[size]; 161 for (int i = 0; i < size; i++) { 162 final ParsedProvider pr = pkg.getProviders() 163 .get(i); 164 if (ComponentParseUtils.isMatch(state, false, pkg.isEnabled(), pr, 165 flags)) { 166 res[num++] = generateProviderInfo(pkg, pr, flags, state, 167 applicationInfo, userId); 168 } 169 } 170 info.providers = ArrayUtils.trimToSize(res, num); 171 } 172 } 173 if ((flags & PackageManager.GET_INSTRUMENTATION) != 0) { 174 int N = pkg.getInstrumentations().size(); 175 if (N > 0) { 176 info.instrumentation = new InstrumentationInfo[N]; 177 for (int i = 0; i < N; i++) { 178 info.instrumentation[i] = generateInstrumentationInfo( 179 pkg.getInstrumentations().get(i), pkg, flags, userId, 180 true /* assignUserFields */); 181 } 182 } 183 } 184 185 return info; 186 } 187 188 @Nullable generateWithoutComponents(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo)189 public static PackageInfo generateWithoutComponents(ParsingPackageRead pkg, int[] gids, 190 @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, 191 Set<String> grantedPermissions, PackageUserState state, int userId, 192 @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) { 193 if (!checkUseInstalled(pkg, state, flags)) { 194 return null; 195 } 196 197 return generateWithoutComponentsUnchecked(pkg, gids, flags, firstInstallTime, 198 lastUpdateTime, grantedPermissions, state, userId, apexInfo, applicationInfo); 199 } 200 201 /** 202 * This bypasses critical checks that are necessary for usage with data passed outside of 203 * system server. 204 * 205 * Prefer {@link #generateWithoutComponents(ParsingPackageRead, int[], int, long, long, Set, 206 * PackageUserState, int, ApexInfo, ApplicationInfo)}. 207 */ 208 @NonNull generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids, @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, Set<String> grantedPermissions, PackageUserState state, int userId, @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo)209 public static PackageInfo generateWithoutComponentsUnchecked(ParsingPackageRead pkg, int[] gids, 210 @PackageManager.PackageInfoFlags int flags, long firstInstallTime, long lastUpdateTime, 211 Set<String> grantedPermissions, PackageUserState state, int userId, 212 @Nullable ApexInfo apexInfo, @NonNull ApplicationInfo applicationInfo) { 213 PackageInfo pi = new PackageInfo(); 214 pi.packageName = pkg.getPackageName(); 215 pi.splitNames = pkg.getSplitNames(); 216 pi.versionCode = pkg.getVersionCode(); 217 pi.versionCodeMajor = pkg.getVersionCodeMajor(); 218 pi.baseRevisionCode = pkg.getBaseRevisionCode(); 219 pi.splitRevisionCodes = pkg.getSplitRevisionCodes(); 220 pi.versionName = pkg.getVersionName(); 221 pi.sharedUserId = pkg.getSharedUserId(); 222 pi.sharedUserLabel = pkg.getSharedUserLabel(); 223 pi.applicationInfo = applicationInfo; 224 pi.installLocation = pkg.getInstallLocation(); 225 if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 226 || (pi.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { 227 pi.requiredForAllUsers = pkg.isRequiredForAllUsers(); 228 } 229 pi.restrictedAccountType = pkg.getRestrictedAccountType(); 230 pi.requiredAccountType = pkg.getRequiredAccountType(); 231 pi.overlayTarget = pkg.getOverlayTarget(); 232 pi.targetOverlayableName = pkg.getOverlayTargetName(); 233 pi.overlayCategory = pkg.getOverlayCategory(); 234 pi.overlayPriority = pkg.getOverlayPriority(); 235 pi.mOverlayIsStatic = pkg.isOverlayIsStatic(); 236 pi.compileSdkVersion = pkg.getCompileSdkVersion(); 237 pi.compileSdkVersionCodename = pkg.getCompileSdkVersionCodeName(); 238 pi.firstInstallTime = firstInstallTime; 239 pi.lastUpdateTime = lastUpdateTime; 240 if ((flags & PackageManager.GET_GIDS) != 0) { 241 pi.gids = gids; 242 } 243 if ((flags & PackageManager.GET_CONFIGURATIONS) != 0) { 244 int size = pkg.getConfigPreferences().size(); 245 if (size > 0) { 246 pi.configPreferences = new ConfigurationInfo[size]; 247 pkg.getConfigPreferences().toArray(pi.configPreferences); 248 } 249 size = pkg.getReqFeatures().size(); 250 if (size > 0) { 251 pi.reqFeatures = new FeatureInfo[size]; 252 pkg.getReqFeatures().toArray(pi.reqFeatures); 253 } 254 size = pkg.getFeatureGroups().size(); 255 if (size > 0) { 256 pi.featureGroups = new FeatureGroupInfo[size]; 257 pkg.getFeatureGroups().toArray(pi.featureGroups); 258 } 259 } 260 if ((flags & PackageManager.GET_PERMISSIONS) != 0) { 261 int size = ArrayUtils.size(pkg.getPermissions()); 262 if (size > 0) { 263 pi.permissions = new PermissionInfo[size]; 264 for (int i = 0; i < size; i++) { 265 pi.permissions[i] = generatePermissionInfo(pkg.getPermissions().get(i), 266 flags); 267 } 268 } 269 final List<ParsedUsesPermission> usesPermissions = pkg.getUsesPermissions(); 270 size = usesPermissions.size(); 271 if (size > 0) { 272 pi.requestedPermissions = new String[size]; 273 pi.requestedPermissionsFlags = new int[size]; 274 for (int i = 0; i < size; i++) { 275 final ParsedUsesPermission usesPermission = usesPermissions.get(i); 276 pi.requestedPermissions[i] = usesPermission.name; 277 // The notion of required permissions is deprecated but for compatibility. 278 pi.requestedPermissionsFlags[i] |= 279 PackageInfo.REQUESTED_PERMISSION_REQUIRED; 280 if (grantedPermissions != null 281 && grantedPermissions.contains(usesPermission.name)) { 282 pi.requestedPermissionsFlags[i] |= 283 PackageInfo.REQUESTED_PERMISSION_GRANTED; 284 } 285 if ((usesPermission.usesPermissionFlags 286 & ParsedUsesPermission.FLAG_NEVER_FOR_LOCATION) != 0) { 287 pi.requestedPermissionsFlags[i] |= 288 PackageInfo.REQUESTED_PERMISSION_NEVER_FOR_LOCATION; 289 } 290 } 291 } 292 } 293 if ((flags & PackageManager.GET_ATTRIBUTIONS) != 0) { 294 int size = ArrayUtils.size(pkg.getAttributions()); 295 if (size > 0) { 296 pi.attributions = new Attribution[size]; 297 for (int i = 0; i < size; i++) { 298 pi.attributions[i] = generateAttribution(pkg.getAttributions().get(i)); 299 } 300 } 301 if (pkg.areAttributionsUserVisible()) { 302 pi.applicationInfo.privateFlagsExt 303 |= ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE; 304 } else { 305 pi.applicationInfo.privateFlagsExt 306 &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE; 307 } 308 } else { 309 pi.applicationInfo.privateFlagsExt 310 &= ~ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE; 311 } 312 313 if (apexInfo != null) { 314 File apexFile = new File(apexInfo.modulePath); 315 316 pi.applicationInfo.sourceDir = apexFile.getPath(); 317 pi.applicationInfo.publicSourceDir = apexFile.getPath(); 318 if (apexInfo.isFactory) { 319 pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; 320 pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 321 } else { 322 pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM; 323 pi.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; 324 } 325 if (apexInfo.isActive) { 326 pi.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; 327 } else { 328 pi.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED; 329 } 330 pi.isApex = true; 331 } 332 333 PackageParser.SigningDetails signingDetails = pkg.getSigningDetails(); 334 // deprecated method of getting signing certificates 335 if ((flags & PackageManager.GET_SIGNATURES) != 0) { 336 if (signingDetails.hasPastSigningCertificates()) { 337 // Package has included signing certificate rotation information. Return the oldest 338 // cert so that programmatic checks keep working even if unaware of key rotation. 339 pi.signatures = new Signature[1]; 340 pi.signatures[0] = signingDetails.pastSigningCertificates[0]; 341 } else if (signingDetails.hasSignatures()) { 342 // otherwise keep old behavior 343 int numberOfSigs = signingDetails.signatures.length; 344 pi.signatures = new Signature[numberOfSigs]; 345 System.arraycopy(signingDetails.signatures, 0, pi.signatures, 0, 346 numberOfSigs); 347 } 348 } 349 350 // replacement for GET_SIGNATURES 351 if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) { 352 if (signingDetails != PackageParser.SigningDetails.UNKNOWN) { 353 // only return a valid SigningInfo if there is signing information to report 354 pi.signingInfo = new SigningInfo(signingDetails); 355 } else { 356 pi.signingInfo = null; 357 } 358 } 359 360 return pi; 361 } 362 363 @Nullable generateApplicationInfo(ParsingPackageRead pkg, @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId)364 public static ApplicationInfo generateApplicationInfo(ParsingPackageRead pkg, 365 @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId) { 366 if (pkg == null) { 367 return null; 368 } 369 370 if (!checkUseInstalled(pkg, state, flags)) { 371 return null; 372 } 373 374 return generateApplicationInfoUnchecked(pkg, flags, state, userId, 375 true /* assignUserFields */); 376 } 377 378 /** 379 * This bypasses critical checks that are necessary for usage with data passed outside of 380 * system server. 381 * 382 * Prefer {@link #generateApplicationInfo(ParsingPackageRead, int, PackageUserState, int)}. 383 * 384 * @param assignUserFields whether to fill the returned {@link ApplicationInfo} with user 385 * specific fields. This can be skipped when building from a system 386 * server package, as there are cached strings which can be used rather 387 * than querying and concatenating the comparatively expensive 388 * {@link Environment#getDataDirectory(String)}}. 389 */ 390 @NonNull generateApplicationInfoUnchecked(@onNull ParsingPackageRead pkg, @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId, boolean assignUserFields)391 public static ApplicationInfo generateApplicationInfoUnchecked(@NonNull ParsingPackageRead pkg, 392 @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId, 393 boolean assignUserFields) { 394 // Make shallow copy so we can store the metadata/libraries safely 395 ApplicationInfo ai = pkg.toAppInfoWithoutState(); 396 397 if (assignUserFields) { 398 assignUserFields(pkg, ai, userId); 399 } 400 401 if ((flags & PackageManager.GET_META_DATA) == 0) { 402 ai.metaData = null; 403 } 404 if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) == 0) { 405 ai.sharedLibraryFiles = null; 406 ai.sharedLibraryInfos = null; 407 } 408 409 // CompatibilityMode is global state. 410 if (!android.content.pm.PackageParser.sCompatibilityModeEnabled) { 411 ai.disableCompatibilityMode(); 412 } 413 414 ai.flags |= flag(state.stopped, ApplicationInfo.FLAG_STOPPED) 415 | flag(state.installed, ApplicationInfo.FLAG_INSTALLED) 416 | flag(state.suspended, ApplicationInfo.FLAG_SUSPENDED); 417 ai.privateFlags |= flag(state.instantApp, ApplicationInfo.PRIVATE_FLAG_INSTANT) 418 | flag(state.virtualPreload, ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD) 419 | flag(state.hidden, ApplicationInfo.PRIVATE_FLAG_HIDDEN); 420 421 if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { 422 ai.enabled = true; 423 } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { 424 ai.enabled = (flags & PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0; 425 } else if (state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED 426 || state.enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) { 427 ai.enabled = false; 428 } 429 ai.enabledSetting = state.enabled; 430 if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { 431 ai.category = state.categoryHint; 432 } 433 if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { 434 ai.category = FallbackCategoryProvider.getFallbackCategory(ai.packageName); 435 } 436 ai.seInfoUser = SELinuxUtil.assignSeinfoUser(state); 437 final OverlayPaths overlayPaths = state.getAllOverlayPaths(); 438 if (overlayPaths != null) { 439 ai.resourceDirs = overlayPaths.getResourceDirs().toArray(new String[0]); 440 ai.overlayPaths = overlayPaths.getOverlayPaths().toArray(new String[0]); 441 } 442 443 return ai; 444 } 445 446 @Nullable generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId)447 public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a, 448 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, 449 @Nullable ApplicationInfo applicationInfo, int userId) { 450 if (a == null) return null; 451 if (!checkUseInstalled(pkg, state, flags)) { 452 return null; 453 } 454 if (applicationInfo == null) { 455 applicationInfo = generateApplicationInfo(pkg, flags, state, userId); 456 } 457 if (applicationInfo == null) { 458 return null; 459 } 460 461 return generateActivityInfoUnchecked(a, flags, applicationInfo); 462 } 463 464 /** 465 * This bypasses critical checks that are necessary for usage with data passed outside of 466 * system server. 467 * 468 * Prefer {@link #generateActivityInfo(ParsingPackageRead, ParsedActivity, int, 469 * PackageUserState, ApplicationInfo, int)}. 470 */ 471 @NonNull generateActivityInfoUnchecked(@onNull ParsedActivity a, @PackageManager.ComponentInfoFlags int flags, @NonNull ApplicationInfo applicationInfo)472 public static ActivityInfo generateActivityInfoUnchecked(@NonNull ParsedActivity a, 473 @PackageManager.ComponentInfoFlags int flags, 474 @NonNull ApplicationInfo applicationInfo) { 475 // Make shallow copies so we can store the metadata safely 476 ActivityInfo ai = new ActivityInfo(); 477 assignSharedFieldsForComponentInfo(ai, a); 478 ai.targetActivity = a.getTargetActivity(); 479 ai.processName = a.getProcessName(); 480 ai.exported = a.isExported(); 481 ai.theme = a.getTheme(); 482 ai.uiOptions = a.getUiOptions(); 483 ai.parentActivityName = a.getParentActivityName(); 484 ai.permission = a.getPermission(); 485 ai.taskAffinity = a.getTaskAffinity(); 486 ai.flags = a.getFlags(); 487 ai.privateFlags = a.getPrivateFlags(); 488 ai.launchMode = a.getLaunchMode(); 489 ai.documentLaunchMode = a.getDocumentLaunchMode(); 490 ai.maxRecents = a.getMaxRecents(); 491 ai.configChanges = a.getConfigChanges(); 492 ai.softInputMode = a.getSoftInputMode(); 493 ai.persistableMode = a.getPersistableMode(); 494 ai.lockTaskLaunchMode = a.getLockTaskLaunchMode(); 495 ai.screenOrientation = a.getScreenOrientation(); 496 ai.resizeMode = a.getResizeMode(); 497 Float maxAspectRatio = a.getMaxAspectRatio(); 498 ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f); 499 Float minAspectRatio = a.getMinAspectRatio(); 500 ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f); 501 ai.supportsSizeChanges = a.getSupportsSizeChanges(); 502 ai.requestedVrComponent = a.getRequestedVrComponent(); 503 ai.rotationAnimation = a.getRotationAnimation(); 504 ai.colorMode = a.getColorMode(); 505 ai.windowLayout = a.getWindowLayout(); 506 ai.attributionTags = a.getAttributionTags(); 507 if ((flags & PackageManager.GET_META_DATA) != 0) { 508 ai.metaData = a.getMetaData(); 509 } 510 ai.applicationInfo = applicationInfo; 511 return ai; 512 } 513 514 @Nullable generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId)515 public static ActivityInfo generateActivityInfo(ParsingPackageRead pkg, ParsedActivity a, 516 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) { 517 return generateActivityInfo(pkg, a, flags, state, null, userId); 518 } 519 520 @Nullable generateServiceInfo(ParsingPackageRead pkg, ParsedService s, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId)521 public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s, 522 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, 523 @Nullable ApplicationInfo applicationInfo, int userId) { 524 if (s == null) return null; 525 if (!checkUseInstalled(pkg, state, flags)) { 526 return null; 527 } 528 if (applicationInfo == null) { 529 applicationInfo = generateApplicationInfo(pkg, flags, state, userId); 530 } 531 if (applicationInfo == null) { 532 return null; 533 } 534 535 return generateServiceInfoUnchecked(s, flags, applicationInfo); 536 } 537 538 /** 539 * This bypasses critical checks that are necessary for usage with data passed outside of 540 * system server. 541 * 542 * Prefer {@link #generateServiceInfo(ParsingPackageRead, ParsedService, int, PackageUserState, 543 * ApplicationInfo, int)}. 544 */ 545 @NonNull generateServiceInfoUnchecked(@onNull ParsedService s, @PackageManager.ComponentInfoFlags int flags, @NonNull ApplicationInfo applicationInfo)546 public static ServiceInfo generateServiceInfoUnchecked(@NonNull ParsedService s, 547 @PackageManager.ComponentInfoFlags int flags, 548 @NonNull ApplicationInfo applicationInfo) { 549 // Make shallow copies so we can store the metadata safely 550 ServiceInfo si = new ServiceInfo(); 551 assignSharedFieldsForComponentInfo(si, s); 552 si.exported = s.isExported(); 553 si.flags = s.getFlags(); 554 si.permission = s.getPermission(); 555 si.processName = s.getProcessName(); 556 si.mForegroundServiceType = s.getForegroundServiceType(); 557 si.applicationInfo = applicationInfo; 558 if ((flags & PackageManager.GET_META_DATA) != 0) { 559 si.metaData = s.getMetaData(); 560 } 561 return si; 562 } 563 564 @Nullable generateServiceInfo(ParsingPackageRead pkg, ParsedService s, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId)565 public static ServiceInfo generateServiceInfo(ParsingPackageRead pkg, ParsedService s, 566 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) { 567 return generateServiceInfo(pkg, s, flags, state, null, userId); 568 } 569 570 @Nullable generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId)571 public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p, 572 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, 573 @Nullable ApplicationInfo applicationInfo, int userId) { 574 if (p == null) return null; 575 if (!checkUseInstalled(pkg, state, flags)) { 576 return null; 577 } 578 if (applicationInfo == null) { 579 applicationInfo = generateApplicationInfo(pkg, flags, state, userId); 580 } 581 if (applicationInfo == null) { 582 return null; 583 } 584 585 return generateProviderInfoUnchecked(p, flags, applicationInfo); 586 } 587 588 /** 589 * This bypasses critical checks that are necessary for usage with data passed outside of 590 * system server. 591 * 592 * Prefer {@link #generateProviderInfo(ParsingPackageRead, ParsedProvider, int, 593 * PackageUserState, ApplicationInfo, int)}. 594 */ 595 @NonNull generateProviderInfoUnchecked(@onNull ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, @NonNull ApplicationInfo applicationInfo)596 public static ProviderInfo generateProviderInfoUnchecked(@NonNull ParsedProvider p, 597 @PackageManager.ComponentInfoFlags int flags, 598 @NonNull ApplicationInfo applicationInfo) { 599 // Make shallow copies so we can store the metadata safely 600 ProviderInfo pi = new ProviderInfo(); 601 assignSharedFieldsForComponentInfo(pi, p); 602 pi.exported = p.isExported(); 603 pi.flags = p.getFlags(); 604 pi.processName = p.getProcessName(); 605 pi.authority = p.getAuthority(); 606 pi.isSyncable = p.isSyncable(); 607 pi.readPermission = p.getReadPermission(); 608 pi.writePermission = p.getWritePermission(); 609 pi.grantUriPermissions = p.isGrantUriPermissions(); 610 pi.forceUriPermissions = p.isForceUriPermissions(); 611 pi.multiprocess = p.isMultiProcess(); 612 pi.initOrder = p.getInitOrder(); 613 pi.uriPermissionPatterns = p.getUriPermissionPatterns(); 614 pi.pathPermissions = p.getPathPermissions(); 615 if ((flags & PackageManager.GET_URI_PERMISSION_PATTERNS) == 0) { 616 pi.uriPermissionPatterns = null; 617 } 618 if ((flags & PackageManager.GET_META_DATA) != 0) { 619 pi.metaData = p.getMetaData(); 620 } 621 pi.applicationInfo = applicationInfo; 622 return pi; 623 } 624 625 @Nullable generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId)626 public static ProviderInfo generateProviderInfo(ParsingPackageRead pkg, ParsedProvider p, 627 @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId) { 628 return generateProviderInfo(pkg, p, flags, state, null, userId); 629 } 630 631 /** 632 * @param assignUserFields see {@link #generateApplicationInfoUnchecked(ParsingPackageRead, int, 633 * PackageUserState, int, boolean)} 634 */ 635 @Nullable generateInstrumentationInfo(ParsedInstrumentation i, ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId, boolean assignUserFields)636 public static InstrumentationInfo generateInstrumentationInfo(ParsedInstrumentation i, 637 ParsingPackageRead pkg, @PackageManager.ComponentInfoFlags int flags, int userId, 638 boolean assignUserFields) { 639 if (i == null) return null; 640 641 InstrumentationInfo ii = new InstrumentationInfo(); 642 assignSharedFieldsForPackageItemInfo(ii, i); 643 ii.targetPackage = i.getTargetPackage(); 644 ii.targetProcesses = i.getTargetProcesses(); 645 ii.handleProfiling = i.isHandleProfiling(); 646 ii.functionalTest = i.isFunctionalTest(); 647 648 ii.sourceDir = pkg.getBaseApkPath(); 649 ii.publicSourceDir = pkg.getBaseApkPath(); 650 ii.splitNames = pkg.getSplitNames(); 651 ii.splitSourceDirs = pkg.getSplitCodePaths(); 652 ii.splitPublicSourceDirs = pkg.getSplitCodePaths(); 653 ii.splitDependencies = pkg.getSplitDependencies(); 654 655 if (assignUserFields) { 656 assignUserFields(pkg, ii, userId); 657 } 658 659 if ((flags & PackageManager.GET_META_DATA) == 0) { 660 return ii; 661 } 662 ii.metaData = i.getMetaData(); 663 return ii; 664 } 665 666 @Nullable generatePermissionInfo(ParsedPermission p, @PackageManager.ComponentInfoFlags int flags)667 public static PermissionInfo generatePermissionInfo(ParsedPermission p, 668 @PackageManager.ComponentInfoFlags int flags) { 669 if (p == null) return null; 670 671 PermissionInfo pi = new PermissionInfo(p.getBackgroundPermission()); 672 673 assignSharedFieldsForPackageItemInfo(pi, p); 674 675 pi.group = p.getGroup(); 676 pi.requestRes = p.getRequestRes(); 677 pi.protectionLevel = p.getProtectionLevel(); 678 pi.descriptionRes = p.getDescriptionRes(); 679 pi.flags = p.getFlags(); 680 pi.knownCerts = p.getKnownCerts(); 681 682 if ((flags & PackageManager.GET_META_DATA) == 0) { 683 return pi; 684 } 685 pi.metaData = p.getMetaData(); 686 return pi; 687 } 688 689 @Nullable generatePermissionGroupInfo(ParsedPermissionGroup pg, @PackageManager.ComponentInfoFlags int flags)690 public static PermissionGroupInfo generatePermissionGroupInfo(ParsedPermissionGroup pg, 691 @PackageManager.ComponentInfoFlags int flags) { 692 if (pg == null) return null; 693 694 PermissionGroupInfo pgi = new PermissionGroupInfo( 695 pg.getRequestDetailResourceId(), 696 pg.getBackgroundRequestResourceId(), 697 pg.getBackgroundRequestDetailResourceId() 698 ); 699 700 assignSharedFieldsForPackageItemInfo(pgi, pg); 701 pgi.descriptionRes = pg.getDescriptionRes(); 702 pgi.priority = pg.getPriority(); 703 pgi.requestRes = pg.getRequestRes(); 704 pgi.flags = pg.getFlags(); 705 706 if ((flags & PackageManager.GET_META_DATA) == 0) { 707 return pgi; 708 } 709 pgi.metaData = pg.getMetaData(); 710 return pgi; 711 } 712 713 @Nullable generateAttribution(ParsedAttribution pa)714 public static Attribution generateAttribution(ParsedAttribution pa) { 715 if (pa == null) return null; 716 return new Attribution(pa.tag, pa.label); 717 } 718 assignSharedFieldsForComponentInfo(@onNull ComponentInfo componentInfo, @NonNull ParsedMainComponent mainComponent)719 private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo, 720 @NonNull ParsedMainComponent mainComponent) { 721 assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent); 722 componentInfo.descriptionRes = mainComponent.getDescriptionRes(); 723 componentInfo.directBootAware = mainComponent.isDirectBootAware(); 724 componentInfo.enabled = mainComponent.isEnabled(); 725 componentInfo.splitName = mainComponent.getSplitName(); 726 componentInfo.attributionTags = mainComponent.getAttributionTags(); 727 } 728 assignSharedFieldsForPackageItemInfo( @onNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component)729 private static void assignSharedFieldsForPackageItemInfo( 730 @NonNull PackageItemInfo packageItemInfo, @NonNull ParsedComponent component) { 731 packageItemInfo.nonLocalizedLabel = ComponentParseUtils.getNonLocalizedLabel(component); 732 packageItemInfo.icon = ComponentParseUtils.getIcon(component); 733 734 packageItemInfo.banner = component.getBanner(); 735 packageItemInfo.labelRes = component.getLabelRes(); 736 packageItemInfo.logo = component.getLogo(); 737 packageItemInfo.name = component.getName(); 738 packageItemInfo.packageName = component.getPackageName(); 739 } 740 741 @CheckResult flag(boolean hasFlag, int flag)742 private static int flag(boolean hasFlag, int flag) { 743 if (hasFlag) { 744 return flag; 745 } else { 746 return 0; 747 } 748 } 749 750 /** @see ApplicationInfo#flags */ appInfoFlags(ParsingPackageRead pkg)751 public static int appInfoFlags(ParsingPackageRead pkg) { 752 // @formatter:off 753 return flag(pkg.isExternalStorage(), ApplicationInfo.FLAG_EXTERNAL_STORAGE) 754 | flag(pkg.isBaseHardwareAccelerated(), ApplicationInfo.FLAG_HARDWARE_ACCELERATED) 755 | flag(pkg.isAllowBackup(), ApplicationInfo.FLAG_ALLOW_BACKUP) 756 | flag(pkg.isKillAfterRestore(), ApplicationInfo.FLAG_KILL_AFTER_RESTORE) 757 | flag(pkg.isRestoreAnyVersion(), ApplicationInfo.FLAG_RESTORE_ANY_VERSION) 758 | flag(pkg.isFullBackupOnly(), ApplicationInfo.FLAG_FULL_BACKUP_ONLY) 759 | flag(pkg.isPersistent(), ApplicationInfo.FLAG_PERSISTENT) 760 | flag(pkg.isDebuggable(), ApplicationInfo.FLAG_DEBUGGABLE) 761 | flag(pkg.isVmSafeMode(), ApplicationInfo.FLAG_VM_SAFE_MODE) 762 | flag(pkg.isHasCode(), ApplicationInfo.FLAG_HAS_CODE) 763 | flag(pkg.isAllowTaskReparenting(), ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) 764 | flag(pkg.isAllowClearUserData(), ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) 765 | flag(pkg.isLargeHeap(), ApplicationInfo.FLAG_LARGE_HEAP) 766 | flag(pkg.isUsesCleartextTraffic(), ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) 767 | flag(pkg.isSupportsRtl(), ApplicationInfo.FLAG_SUPPORTS_RTL) 768 | flag(pkg.isTestOnly(), ApplicationInfo.FLAG_TEST_ONLY) 769 | flag(pkg.isMultiArch(), ApplicationInfo.FLAG_MULTIARCH) 770 | flag(pkg.isExtractNativeLibs(), ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) 771 | flag(pkg.isGame(), ApplicationInfo.FLAG_IS_GAME) 772 | flag(pkg.isSupportsSmallScreens(), ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS) 773 | flag(pkg.isSupportsNormalScreens(), ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS) 774 | flag(pkg.isSupportsLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) 775 | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) 776 | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) 777 | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES); 778 // @formatter:on 779 } 780 781 /** @see ApplicationInfo#privateFlags */ appInfoPrivateFlags(ParsingPackageRead pkg)782 public static int appInfoPrivateFlags(ParsingPackageRead pkg) { 783 // @formatter:off 784 int privateFlags = flag(pkg.isStaticSharedLibrary(), ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY) 785 | flag(pkg.isOverlay(), ApplicationInfo.PRIVATE_FLAG_IS_RESOURCE_OVERLAY) 786 | flag(pkg.isIsolatedSplitLoading(), ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING) 787 | flag(pkg.isHasDomainUrls(), ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) 788 | flag(pkg.isProfileableByShell(), ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL) 789 | flag(pkg.isBackupInForeground(), ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND) 790 | flag(pkg.isUseEmbeddedDex(), ApplicationInfo.PRIVATE_FLAG_USE_EMBEDDED_DEX) 791 | flag(pkg.isDefaultToDeviceProtectedStorage(), ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) 792 | flag(pkg.isDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE) 793 | flag(pkg.isPartiallyDirectBootAware(), ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE) 794 | flag(pkg.isAllowClearUserDataOnFailedRestore(), ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) 795 | flag(pkg.isAllowAudioPlaybackCapture(), ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) 796 | flag(pkg.isRequestLegacyExternalStorage(), ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) 797 | flag(pkg.isUsesNonSdkApi(), ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API) 798 | flag(pkg.isHasFragileUserData(), ApplicationInfo.PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) 799 | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) 800 | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) 801 | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING); 802 // @formatter:on 803 804 Boolean resizeableActivity = pkg.getResizeableActivity(); 805 if (resizeableActivity != null) { 806 if (resizeableActivity) { 807 privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; 808 } else { 809 privateFlags |= ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; 810 } 811 } 812 813 return privateFlags; 814 } 815 816 /** @see ApplicationInfo#privateFlagsExt */ appInfoPrivateFlagsExt(ParsingPackageRead pkg)817 public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) { 818 // @formatter:off 819 int privateFlagsExt = 820 flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE) 821 | flag(pkg.hasRequestForegroundServiceExemption(), 822 ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) 823 | flag(pkg.areAttributionsUserVisible(), 824 ApplicationInfo.PRIVATE_FLAG_EXT_ATTRIBUTIONS_ARE_USER_VISIBLE); 825 // @formatter:on 826 return privateFlagsExt; 827 } 828 checkUseInstalled(ParsingPackageRead pkg, PackageUserState state, @PackageManager.PackageInfoFlags int flags)829 private static boolean checkUseInstalled(ParsingPackageRead pkg, PackageUserState state, 830 @PackageManager.PackageInfoFlags int flags) { 831 // If available for the target user 832 return state.isAvailable(flags); 833 } 834 835 @NonNull getDataDir(ParsingPackageRead pkg, int userId)836 public static File getDataDir(ParsingPackageRead pkg, int userId) { 837 if ("android".equals(pkg.getPackageName())) { 838 return Environment.getDataSystemDirectory(); 839 } 840 841 if (pkg.isDefaultToDeviceProtectedStorage() 842 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { 843 return getDeviceProtectedDataDir(pkg, userId); 844 } else { 845 return getCredentialProtectedDataDir(pkg, userId); 846 } 847 } 848 849 @NonNull getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId)850 public static File getDeviceProtectedDataDir(ParsingPackageRead pkg, int userId) { 851 return Environment.getDataUserDePackageDirectory(pkg.getVolumeUuid(), userId, 852 pkg.getPackageName()); 853 } 854 855 @NonNull getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId)856 public static File getCredentialProtectedDataDir(ParsingPackageRead pkg, int userId) { 857 return Environment.getDataUserCePackageDirectory(pkg.getVolumeUuid(), userId, 858 pkg.getPackageName()); 859 } 860 assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId)861 private static void assignUserFields(ParsingPackageRead pkg, ApplicationInfo info, int userId) { 862 // This behavior is undefined for no-state ApplicationInfos when called by a public API, 863 // since the uid is never assigned by the system. It will always effectively be appId 0. 864 info.uid = UserHandle.getUid(userId, UserHandle.getAppId(info.uid)); 865 866 String pkgName = pkg.getPackageName(); 867 if ("android".equals(pkgName)) { 868 info.dataDir = SYSTEM_DATA_PATH; 869 return; 870 } 871 872 // For performance reasons, all these paths are built as strings 873 String baseDataDirPrefix = 874 Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; 875 String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; 876 info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE 877 + userIdPkgSuffix; 878 info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; 879 880 if (pkg.isDefaultToDeviceProtectedStorage() 881 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { 882 info.dataDir = info.deviceProtectedDataDir; 883 } else { 884 info.dataDir = info.credentialProtectedDataDir; 885 } 886 } 887 assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info, int userId)888 private static void assignUserFields(ParsingPackageRead pkg, InstrumentationInfo info, 889 int userId) { 890 String pkgName = pkg.getPackageName(); 891 if ("android".equals(pkgName)) { 892 info.dataDir = SYSTEM_DATA_PATH; 893 return; 894 } 895 896 // For performance reasons, all these paths are built as strings 897 String baseDataDirPrefix = 898 Environment.getDataDirectoryPath(pkg.getVolumeUuid()) + File.separator; 899 String userIdPkgSuffix = File.separator + userId + File.separator + pkgName; 900 info.credentialProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_CE 901 + userIdPkgSuffix; 902 info.deviceProtectedDataDir = baseDataDirPrefix + Environment.DIR_USER_DE + userIdPkgSuffix; 903 904 if (pkg.isDefaultToDeviceProtectedStorage() 905 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { 906 info.dataDir = info.deviceProtectedDataDir; 907 } else { 908 info.dataDir = info.credentialProtectedDataDir; 909 } 910 } 911 } 912