• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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