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.component; 18 19 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 21 import static android.content.pm.parsing.component.ComponentParseUtils.flag; 22 23 import android.annotation.NonNull; 24 import android.app.ActivityTaskManager; 25 import android.content.Intent; 26 import android.content.pm.ActivityInfo; 27 import android.content.pm.parsing.ParsingPackage; 28 import android.content.pm.parsing.ParsingPackageUtils; 29 import android.content.pm.parsing.ParsingUtils; 30 import android.content.pm.parsing.result.ParseInput; 31 import android.content.pm.parsing.result.ParseInput.DeferredError; 32 import android.content.pm.parsing.result.ParseResult; 33 import android.content.res.Configuration; 34 import android.content.res.Resources; 35 import android.content.res.TypedArray; 36 import android.content.res.XmlResourceParser; 37 import android.os.Build; 38 import android.util.ArraySet; 39 import android.util.AttributeSet; 40 import android.util.Log; 41 import android.util.Slog; 42 import android.util.TypedValue; 43 import android.view.Gravity; 44 import android.view.WindowManager; 45 46 import com.android.internal.R; 47 import com.android.internal.annotations.VisibleForTesting; 48 import com.android.internal.util.ArrayUtils; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 53 import java.io.IOException; 54 import java.util.List; 55 import java.util.Objects; 56 import java.util.Set; 57 58 /** @hide */ 59 public class ParsedActivityUtils { 60 61 private static final String TAG = ParsingUtils.TAG; 62 63 public static final boolean LOG_UNSAFE_BROADCASTS = false; 64 65 // Set of broadcast actions that are safe for manifest receivers 66 public static final Set<String> SAFE_BROADCASTS = new ArraySet<>(); 67 static { 68 SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED); 69 } 70 71 /** 72 * Bit mask of all the valid bits that can be set in recreateOnConfigChanges. 73 */ 74 private static final int RECREATE_ON_CONFIG_CHANGES_MASK = 75 ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC; 76 77 @NonNull 78 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) parseActivityOrReceiver(String[] separateProcesses, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, boolean useRoundIcon, ParseInput input)79 public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses, 80 ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags, 81 boolean useRoundIcon, ParseInput input) 82 throws XmlPullParserException, IOException { 83 final String packageName = pkg.getPackageName(); 84 final ParsedActivity 85 activity = new ParsedActivity(); 86 87 boolean receiver = "receiver".equals(parser.getName()); 88 String tag = "<" + parser.getName() + ">"; 89 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity); 90 try { 91 ParseResult<ParsedActivity> result = 92 ParsedMainComponentUtils.parseMainComponent( 93 activity, tag, separateProcesses, 94 pkg, sa, flags, useRoundIcon, input, 95 R.styleable.AndroidManifestActivity_banner, 96 R.styleable.AndroidManifestActivity_description, 97 R.styleable.AndroidManifestActivity_directBootAware, 98 R.styleable.AndroidManifestActivity_enabled, 99 R.styleable.AndroidManifestActivity_icon, 100 R.styleable.AndroidManifestActivity_label, 101 R.styleable.AndroidManifestActivity_logo, 102 R.styleable.AndroidManifestActivity_name, 103 R.styleable.AndroidManifestActivity_process, 104 R.styleable.AndroidManifestActivity_roundIcon, 105 R.styleable.AndroidManifestActivity_splitName, 106 R.styleable.AndroidManifestActivity_attributionTags); 107 if (result.isError()) { 108 return result; 109 } 110 111 if (receiver && pkg.isCantSaveState()) { 112 // A heavy-weight application can not have receivers in its main process 113 if (Objects.equals(activity.getProcessName(), packageName)) { 114 return input.error("Heavy-weight applications can not have receivers " 115 + "in main process"); 116 } 117 } 118 119 // The following section has formatting off to make it easier to read the flags. 120 // Multi-lining them to fit within the column restriction makes it hard to tell what 121 // field is assigned where. 122 // @formatter:off 123 activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0); 124 activity.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions, pkg.getUiOptions()); 125 126 activity.flags |= flag(ActivityInfo.FLAG_ALLOW_TASK_REPARENTING, R.styleable.AndroidManifestActivity_allowTaskReparenting, pkg.isAllowTaskReparenting(), sa) 127 | flag(ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE, R.styleable.AndroidManifestActivity_alwaysRetainTaskState, sa) 128 | flag(ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH, R.styleable.AndroidManifestActivity_clearTaskOnLaunch, sa) 129 | flag(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS, R.styleable.AndroidManifestActivity_excludeFromRecents, sa) 130 | flag(ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS, R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs, sa) 131 | flag(ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH, R.styleable.AndroidManifestActivity_finishOnTaskLaunch, sa) 132 | flag(ActivityInfo.FLAG_IMMERSIVE, R.styleable.AndroidManifestActivity_immersive, sa) 133 | flag(ActivityInfo.FLAG_MULTIPROCESS, R.styleable.AndroidManifestActivity_multiprocess, sa) 134 | flag(ActivityInfo.FLAG_NO_HISTORY, R.styleable.AndroidManifestActivity_noHistory, sa) 135 | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showForAllUsers, sa) 136 | flag(ActivityInfo.FLAG_SHOW_FOR_ALL_USERS, R.styleable.AndroidManifestActivity_showOnLockScreen, sa) 137 | flag(ActivityInfo.FLAG_STATE_NOT_NEEDED, R.styleable.AndroidManifestActivity_stateNotNeeded, sa) 138 | flag(ActivityInfo.FLAG_SYSTEM_USER_ONLY, R.styleable.AndroidManifestActivity_systemUserOnly, sa); 139 140 if (!receiver) { 141 activity.flags |= flag(ActivityInfo.FLAG_HARDWARE_ACCELERATED, R.styleable.AndroidManifestActivity_hardwareAccelerated, pkg.isBaseHardwareAccelerated(), sa) 142 | flag(ActivityInfo.FLAG_ALLOW_EMBEDDED, R.styleable.AndroidManifestActivity_allowEmbedded, sa) 143 | flag(ActivityInfo.FLAG_ALWAYS_FOCUSABLE, R.styleable.AndroidManifestActivity_alwaysFocusable, sa) 144 | flag(ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS, R.styleable.AndroidManifestActivity_autoRemoveFromRecents, sa) 145 | flag(ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY, R.styleable.AndroidManifestActivity_relinquishTaskIdentity, sa) 146 | flag(ActivityInfo.FLAG_RESUME_WHILE_PAUSING, R.styleable.AndroidManifestActivity_resumeWhilePausing, sa) 147 | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa) 148 | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa) 149 | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa) 150 | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa); 151 152 activity.privateFlags |= flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED, 153 R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa) 154 | flag(ActivityInfo.PRIVATE_FLAG_HOME_TRANSITION_SOUND, 155 R.styleable.AndroidManifestActivity_playHomeTransitionSound, true, sa); 156 157 activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT); 158 activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE); 159 activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE); 160 activity.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); 161 activity.maxRecents = sa.getInt(R.styleable.AndroidManifestActivity_maxRecents, ActivityTaskManager.getDefaultAppRecentsLimitStatic()); 162 activity.persistableMode = sa.getInteger(R.styleable.AndroidManifestActivity_persistableMode, ActivityInfo.PERSIST_ROOT_ONLY); 163 activity.requestedVrComponent = sa.getString(R.styleable.AndroidManifestActivity_enableVrMode); 164 activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED); 165 activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); 166 167 activity.configChanges = getActivityConfigChanges( 168 sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0), 169 sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0)); 170 171 int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED); 172 int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation); 173 activity.screenOrientation = screenOrientation; 174 activity.resizeMode = resizeMode; 175 176 if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio) 177 && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio) 178 == TypedValue.TYPE_FLOAT) { 179 activity.setMaxAspectRatio(resizeMode, 180 sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio, 181 0 /*default*/)); 182 } 183 184 if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio) 185 && sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio) 186 == TypedValue.TYPE_FLOAT) { 187 activity.setMinAspectRatio(resizeMode, 188 sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio, 189 0 /*default*/)); 190 } 191 } else { 192 activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; 193 activity.configChanges = 0; 194 activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa); 195 } 196 // @formatter:on 197 198 String taskAffinity = sa.getNonConfigurationString( 199 R.styleable.AndroidManifestActivity_taskAffinity, 200 Configuration.NATIVE_CONFIG_VERSION); 201 202 ParseResult<String> affinityNameResult = ComponentParseUtils.buildTaskAffinityName( 203 packageName, pkg.getTaskAffinity(), taskAffinity, input); 204 if (affinityNameResult.isError()) { 205 return input.error(affinityNameResult); 206 } 207 208 activity.taskAffinity = affinityNameResult.getResult(); 209 210 boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false); 211 if (visibleToEphemeral) { 212 activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; 213 pkg.setVisibleToInstantApps(true); 214 } 215 216 return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver, 217 false /*isAlias*/, visibleToEphemeral, input, 218 R.styleable.AndroidManifestActivity_parentActivityName, 219 R.styleable.AndroidManifestActivity_permission, 220 R.styleable.AndroidManifestActivity_exported 221 ); 222 } finally { 223 sa.recycle(); 224 } 225 } 226 227 @NonNull parseActivityAlias(ParsingPackage pkg, Resources res, XmlResourceParser parser, boolean useRoundIcon, ParseInput input)228 public static ParseResult<ParsedActivity> parseActivityAlias(ParsingPackage pkg, Resources res, 229 XmlResourceParser parser, boolean useRoundIcon, ParseInput input) 230 throws XmlPullParserException, IOException { 231 TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivityAlias); 232 try { 233 String targetActivity = sa.getNonConfigurationString( 234 R.styleable.AndroidManifestActivityAlias_targetActivity, 235 Configuration.NATIVE_CONFIG_VERSION); 236 if (targetActivity == null) { 237 return input.error("<activity-alias> does not specify android:targetActivity"); 238 } 239 240 String packageName = pkg.getPackageName(); 241 targetActivity = ParsingUtils.buildClassName(packageName, targetActivity); 242 if (targetActivity == null) { 243 return input.error("Empty class name in package " + packageName); 244 } 245 246 ParsedActivity target = null; 247 248 List<ParsedActivity> activities = pkg.getActivities(); 249 final int activitiesSize = ArrayUtils.size(activities); 250 for (int i = 0; i < activitiesSize; i++) { 251 ParsedActivity t = activities.get(i); 252 if (targetActivity.equals(t.getName())) { 253 target = t; 254 break; 255 } 256 } 257 258 if (target == null) { 259 return input.error("<activity-alias> target activity " + targetActivity 260 + " not found in manifest with activities = " 261 + pkg.getActivities() 262 + ", parsedActivities = " + activities); 263 } 264 265 ParsedActivity activity = ParsedActivity.makeAlias(targetActivity, target); 266 String tag = "<" + parser.getName() + ">"; 267 268 ParseResult<ParsedActivity> result = ParsedMainComponentUtils.parseMainComponent( 269 activity, tag, null, pkg, sa, 0, useRoundIcon, input, 270 R.styleable.AndroidManifestActivityAlias_banner, 271 R.styleable.AndroidManifestActivityAlias_description, 272 null /*directBootAwareAttr*/, 273 R.styleable.AndroidManifestActivityAlias_enabled, 274 R.styleable.AndroidManifestActivityAlias_icon, 275 R.styleable.AndroidManifestActivityAlias_label, 276 R.styleable.AndroidManifestActivityAlias_logo, 277 R.styleable.AndroidManifestActivityAlias_name, 278 null /*processAttr*/, 279 R.styleable.AndroidManifestActivityAlias_roundIcon, 280 null /*splitNameAttr*/, 281 R.styleable.AndroidManifestActivityAlias_attributionTags); 282 if (result.isError()) { 283 return result; 284 } 285 286 // TODO add visibleToInstantApps attribute to activity alias 287 final boolean visibleToEphemeral = 288 ((activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0); 289 290 return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, false /*isReceiver*/, true /*isAlias*/, 291 visibleToEphemeral, input, 292 R.styleable.AndroidManifestActivityAlias_parentActivityName, 293 R.styleable.AndroidManifestActivityAlias_permission, 294 R.styleable.AndroidManifestActivityAlias_exported); 295 } finally { 296 sa.recycle(); 297 } 298 } 299 300 /** 301 * This method shares parsing logic between Activity/Receiver/alias instances, but requires 302 * passing in booleans for isReceiver/isAlias, since there's no indicator in the other 303 * parameters. 304 * 305 * They're used to filter the parsed tags and their behavior. This makes the method rather 306 * messy, but it's more maintainable than writing 3 separate methods for essentially the same 307 * type of logic. 308 */ 309 @NonNull parseActivityOrAlias(ParsedActivity activity, ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources, TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral, ParseInput input, int parentActivityNameAttr, int permissionAttr, int exportedAttr)310 private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity, 311 ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources, 312 TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral, 313 ParseInput input, int parentActivityNameAttr, int permissionAttr, 314 int exportedAttr) throws IOException, XmlPullParserException { 315 String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION); 316 if (parentActivityName != null) { 317 String packageName = pkg.getPackageName(); 318 String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName); 319 if (parentClassName == null) { 320 Log.e(TAG, "Activity " + activity.getName() 321 + " specified invalid parentActivityName " + parentActivityName); 322 } else { 323 activity.setParentActivity(parentClassName); 324 } 325 } 326 327 String permission = array.getNonConfigurationString(permissionAttr, 0); 328 if (isAlias) { 329 // An alias will override permissions to allow referencing an Activity through its alias 330 // without needing the original permission. If an alias needs the same permission, 331 // it must be re-declared. 332 activity.setPermission(permission); 333 } else { 334 activity.setPermission(permission != null ? permission : pkg.getPermission()); 335 } 336 337 final boolean setExported = array.hasValue(exportedAttr); 338 if (setExported) { 339 activity.exported = array.getBoolean(exportedAttr, false); 340 } 341 342 final int depth = parser.getDepth(); 343 int type; 344 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 345 && (type != XmlPullParser.END_TAG 346 || parser.getDepth() > depth)) { 347 if (type != XmlPullParser.START_TAG) { 348 continue; 349 } 350 351 final ParseResult result; 352 if (parser.getName().equals("intent-filter")) { 353 ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity, 354 !isReceiver, visibleToEphemeral, resources, parser, input); 355 if (intentResult.isSuccess()) { 356 ParsedIntentInfo intent = intentResult.getResult(); 357 if (intent != null) { 358 activity.order = Math.max(intent.getOrder(), activity.order); 359 activity.addIntent(intent); 360 if (LOG_UNSAFE_BROADCASTS && isReceiver 361 && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) { 362 int actionCount = intent.countActions(); 363 for (int i = 0; i < actionCount; i++) { 364 final String action = intent.getAction(i); 365 if (action == null || !action.startsWith("android.")) { 366 continue; 367 } 368 369 if (!SAFE_BROADCASTS.contains(action)) { 370 Slog.w(TAG, 371 "Broadcast " + action + " may never be delivered to " 372 + pkg.getPackageName() + " as requested at: " 373 + parser.getPositionDescription()); 374 } 375 } 376 } 377 } 378 } 379 result = intentResult; 380 } else if (parser.getName().equals("meta-data")) { 381 result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input); 382 } else if (parser.getName().equals("property")) { 383 result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input); 384 } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) { 385 ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity, 386 true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral, 387 resources, parser, input); 388 if (intentResult.isSuccess()) { 389 ParsedIntentInfo intent = intentResult.getResult(); 390 if (intent != null) { 391 pkg.addPreferredActivityFilter(activity.getClassName(), intent); 392 } 393 } 394 result = intentResult; 395 } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) { 396 ParseResult<ActivityInfo.WindowLayout> layoutResult = 397 parseActivityWindowLayout(resources, parser, input); 398 if (layoutResult.isSuccess()) { 399 activity.windowLayout = layoutResult.getResult(); 400 } 401 result = layoutResult; 402 } else { 403 result = ParsingUtils.unknownTag(tag, pkg, parser, input); 404 } 405 406 if (result.isError()) { 407 return input.error(result); 408 } 409 } 410 411 if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK 412 && activity.metaData != null && activity.metaData.containsKey( 413 ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) { 414 final String launchMode = activity.metaData.getString( 415 ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE); 416 if (launchMode != null && launchMode.equals("singleInstancePerTask")) { 417 activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK; 418 } 419 } 420 421 ParseResult<ActivityInfo.WindowLayout> layoutResult = 422 resolveActivityWindowLayout(activity, input); 423 if (layoutResult.isError()) { 424 return input.error(layoutResult); 425 } 426 activity.windowLayout = layoutResult.getResult(); 427 428 if (!setExported) { 429 boolean hasIntentFilters = activity.getIntents().size() > 0; 430 if (hasIntentFilters) { 431 final ParseResult exportedCheckResult = input.deferError( 432 activity.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S 433 + " and above) requires that an explicit value for android:exported be" 434 + " defined when intent filters are present", 435 DeferredError.MISSING_EXPORTED_FLAG); 436 if (exportedCheckResult.isError()) { 437 return input.error(exportedCheckResult); 438 } 439 } 440 activity.exported = hasIntentFilters; 441 } 442 443 return input.success(activity); 444 } 445 446 @NonNull parseIntentFilter(ParsingPackage pkg, ParsedActivity activity, boolean allowImplicitEphemeralVisibility, boolean visibleToEphemeral, Resources resources, XmlResourceParser parser, ParseInput input)447 private static ParseResult<ParsedIntentInfo> parseIntentFilter(ParsingPackage pkg, 448 ParsedActivity activity, boolean allowImplicitEphemeralVisibility, 449 boolean visibleToEphemeral, Resources resources, XmlResourceParser parser, 450 ParseInput input) throws IOException, XmlPullParserException { 451 ParseResult<ParsedIntentInfo> result = ParsedMainComponentUtils.parseIntentFilter(activity, 452 pkg, resources, parser, visibleToEphemeral, true /*allowGlobs*/, 453 true /*allowAutoVerify*/, allowImplicitEphemeralVisibility, 454 true /*failOnNoActions*/, input); 455 if (result.isError()) { 456 return input.error(result); 457 } 458 459 ParsedIntentInfo intent = result.getResult(); 460 if (intent != null) { 461 if (intent.isVisibleToInstantApp()) { 462 activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP; 463 } 464 if (intent.isImplicitlyVisibleToInstantApp()) { 465 activity.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP; 466 } 467 } 468 469 return input.success(intent); 470 } 471 getActivityResizeMode(ParsingPackage pkg, TypedArray sa, int screenOrientation)472 private static int getActivityResizeMode(ParsingPackage pkg, TypedArray sa, 473 int screenOrientation) { 474 Boolean resizeableActivity = pkg.getResizeableActivity(); 475 476 if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity) 477 || resizeableActivity != null) { 478 // Activity or app explicitly set if it is resizeable or not; 479 if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity, 480 resizeableActivity != null && resizeableActivity)) { 481 return ActivityInfo.RESIZE_MODE_RESIZEABLE; 482 } else { 483 return ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 484 } 485 } 486 487 if (pkg.isResizeableActivityViaSdkVersion()) { 488 // The activity or app didn't explicitly set the resizing option, however we want to 489 // make it resize due to the sdk version it is targeting. 490 return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; 491 } 492 493 // resize preference isn't set and target sdk version doesn't support resizing apps by 494 // default. For the app to be resizeable if it isn't fixed orientation or immersive. 495 if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) { 496 return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; 497 } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) { 498 return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; 499 } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) { 500 return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; 501 } else { 502 return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; 503 } 504 } 505 506 @NonNull parseActivityWindowLayout(Resources res, AttributeSet attrs, ParseInput input)507 private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res, 508 AttributeSet attrs, ParseInput input) { 509 TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout); 510 try { 511 int width = -1; 512 float widthFraction = -1f; 513 int height = -1; 514 float heightFraction = -1f; 515 final int widthType = sw.getType(R.styleable.AndroidManifestLayout_defaultWidth); 516 if (widthType == TypedValue.TYPE_FRACTION) { 517 widthFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultWidth, 1, 1, 518 -1); 519 } else if (widthType == TypedValue.TYPE_DIMENSION) { 520 width = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultWidth, 521 -1); 522 } 523 final int heightType = sw.getType(R.styleable.AndroidManifestLayout_defaultHeight); 524 if (heightType == TypedValue.TYPE_FRACTION) { 525 heightFraction = sw.getFraction(R.styleable.AndroidManifestLayout_defaultHeight, 1, 526 1, -1); 527 } else if (heightType == TypedValue.TYPE_DIMENSION) { 528 height = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_defaultHeight, 529 -1); 530 } 531 int gravity = sw.getInt(R.styleable.AndroidManifestLayout_gravity, Gravity.CENTER); 532 int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1); 533 int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight, 534 -1); 535 String windowLayoutAffinity = 536 sw.getNonConfigurationString( 537 R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0); 538 final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width, 539 widthFraction, height, heightFraction, gravity, minWidth, minHeight, 540 windowLayoutAffinity); 541 return input.success(windowLayout); 542 } finally { 543 sw.recycle(); 544 } 545 } 546 547 /** 548 * Resolves values in {@link ActivityInfo.WindowLayout}. 549 * 550 * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in 551 * Android R and some variants of pre-R. 552 */ resolveActivityWindowLayout( ParsedActivity activity, ParseInput input)553 private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout( 554 ParsedActivity activity, ParseInput input) { 555 // There isn't a metadata for us to fall back. Whatever is in layout is correct. 556 if (activity.metaData == null || !activity.metaData.containsKey( 557 ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) { 558 return input.success(activity.windowLayout); 559 } 560 561 // Layout already specifies a value. We should just use that one. 562 if (activity.windowLayout != null && activity.windowLayout.windowLayoutAffinity != null) { 563 return input.success(activity.windowLayout); 564 } 565 566 String windowLayoutAffinity = activity.metaData.getString( 567 ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY); 568 ActivityInfo.WindowLayout layout = activity.windowLayout; 569 if (layout == null) { 570 layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */, 571 -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY, 572 -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity); 573 } else { 574 layout.windowLayoutAffinity = windowLayoutAffinity; 575 } 576 return input.success(layout); 577 } 578 579 /** 580 * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml. 581 * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from 582 * AndroidManifest.xml. 583 * @hide 584 */ getActivityConfigChanges(int configChanges, int recreateOnConfigChanges)585 static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) { 586 return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK); 587 } 588 } 589