blob: 9a0a6d54da50646eb24d7d162d61f48f30b787d5 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.pm.parsing;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.ActivityTaskManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
import android.view.Gravity;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
/**
* TODO(b/135203078): Move the inner classes out to separate files.
* TODO(b/135203078): Expose inner classes as immutable through interface methods.
*
* @hide
*/
public class ComponentParseUtils {
private static final String TAG = ApkParseUtils.TAG;
// TODO(b/135203078): None of this class's subclasses do anything. Remove in favor of base?
public static class ParsedIntentInfo extends IntentFilter {
/**
* <p>
* Implementation note: The serialized form for the intent list also contains the name
* of the concrete class that's stored in the list, and assumes that every element of the
* list is of the same type. This is very similar to the original parcelable mechanism.
* We cannot use that directly because IntentInfo extends IntentFilter, which is parcelable
* and is public API. It also declares Parcelable related methods as final which means
* we can't extend them. The approach of using composition instead of inheritance leads to
* a large set of cascading changes in the PackageManagerService, which seem undesirable.
*
* <p>
* <b>WARNING: </b> The list of objects returned by this function might need to be fixed up
* to make sure their owner fields are consistent. See {@code fixupOwner}.
*/
public static void writeIntentsList(List<? extends ParsedIntentInfo> list, Parcel out,
int flags) {
if (list == null) {
out.writeInt(-1);
return;
}
final int size = list.size();
out.writeInt(size);
// Don't bother writing the component name if the list is empty.
if (size > 0) {
ParsedIntentInfo info = list.get(0);
out.writeString(info.getClass().getName());
for (int i = 0; i < size; i++) {
list.get(i).writeIntentInfoToParcel(out, flags);
}
}
}
public static <T extends ParsedIntentInfo> ArrayList<T> createIntentsList(Parcel in) {
int size = in.readInt();
if (size == -1) {
return null;
}
if (size == 0) {
return new ArrayList<>(0);
}
String className = in.readString();
final ArrayList<T> intentsList;
try {
final Class<T> cls = (Class<T>) Class.forName(className);
final Constructor<T> cons = cls.getConstructor(Parcel.class);
intentsList = new ArrayList<>(size);
for (int i = 0; i < size; ++i) {
intentsList.add(cons.newInstance(in));
}
} catch (ReflectiveOperationException ree) {
throw new AssertionError("Unable to construct intent list for: "
+ className, ree);
}
return intentsList;
}
protected String packageName;
protected final String className;
public boolean hasDefault;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int icon;
protected List<String> rawDataTypes;
public void addRawDataType(String dataType) throws MalformedMimeTypeException {
if (rawDataTypes == null) {
rawDataTypes = new ArrayList<>();
}
rawDataTypes.add(dataType);
addDataType(dataType);
}
public ParsedIntentInfo(String packageName, String className) {
this.packageName = packageName;
this.className = className;
}
public ParsedIntentInfo(Parcel in) {
super(in);
packageName = in.readString();
className = in.readString();
hasDefault = (in.readInt() == 1);
labelRes = in.readInt();
nonLocalizedLabel = in.readCharSequence();
icon = in.readInt();
}
public void writeIntentInfoToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(packageName);
dest.writeString(className);
dest.writeInt(hasDefault ? 1 : 0);
dest.writeInt(labelRes);
dest.writeCharSequence(nonLocalizedLabel);
dest.writeInt(icon);
}
public String getPackageName() {
return packageName;
}
public String getClassName() {
return className;
}
}
public static class ParsedActivityIntentInfo extends ParsedIntentInfo {
public ParsedActivityIntentInfo(String packageName, String className) {
super(packageName, className);
}
public ParsedActivityIntentInfo(Parcel in) {
super(in);
}
public static final Creator<ParsedActivityIntentInfo> CREATOR =
new Creator<ParsedActivityIntentInfo>() {
@Override
public ParsedActivityIntentInfo createFromParcel(Parcel source) {
return new ParsedActivityIntentInfo(source);
}
@Override
public ParsedActivityIntentInfo[] newArray(int size) {
return new ParsedActivityIntentInfo[size];
}
};
}
public static class ParsedServiceIntentInfo extends ParsedIntentInfo {
public ParsedServiceIntentInfo(String packageName, String className) {
super(packageName, className);
}
public ParsedServiceIntentInfo(Parcel in) {
super(in);
}
public static final Creator<ParsedServiceIntentInfo> CREATOR =
new Creator<ParsedServiceIntentInfo>() {
@Override
public ParsedServiceIntentInfo createFromParcel(Parcel source) {
return new ParsedServiceIntentInfo(source);
}
@Override
public ParsedServiceIntentInfo[] newArray(int size) {
return new ParsedServiceIntentInfo[size];
}
};
}
public static class ParsedProviderIntentInfo extends ParsedIntentInfo {
public ParsedProviderIntentInfo(String packageName, String className) {
super(packageName, className);
}
public ParsedProviderIntentInfo(Parcel in) {
super(in);
}
public static final Creator<ParsedProviderIntentInfo> CREATOR =
new Creator<ParsedProviderIntentInfo>() {
@Override
public ParsedProviderIntentInfo createFromParcel(Parcel source) {
return new ParsedProviderIntentInfo(source);
}
@Override
public ParsedProviderIntentInfo[] newArray(int size) {
return new ParsedProviderIntentInfo[size];
}
};
}
public static class ParsedQueriesIntentInfo extends ParsedIntentInfo {
public ParsedQueriesIntentInfo(String packageName, String className) {
super(packageName, className);
}
public ParsedQueriesIntentInfo(Parcel in) {
super(in);
}
public static final Creator<ParsedQueriesIntentInfo> CREATOR =
new Creator<ParsedQueriesIntentInfo>() {
@Override
public ParsedQueriesIntentInfo createFromParcel(Parcel source) {
return new ParsedQueriesIntentInfo(source);
}
@Override
public ParsedQueriesIntentInfo[] newArray(int size) {
return new ParsedQueriesIntentInfo[size];
}
};
}
public static class ParsedComponent<IntentInfoType extends ParsedIntentInfo> implements
Parcelable {
// TODO(b/135203078): Replace with "name", as not all usages are an actual class
public String className;
public int icon;
public int labelRes;
public CharSequence nonLocalizedLabel;
public int logo;
public int banner;
public int descriptionRes;
// TODO(b/135203078): Make subclass that contains these fields only for the necessary
// subtypes
protected boolean enabled = true;
protected boolean directBootAware;
public int flags;
private String packageName;
private String splitName;
// TODO(b/135203078): Make nullable
public List<IntentInfoType> intents = new ArrayList<>();
private transient ComponentName componentName;
protected Bundle metaData;
public void setSplitName(String splitName) {
this.splitName = splitName;
}
public String getSplitName() {
return splitName;
}
@CallSuper
public void setPackageName(String packageName) {
this.packageName = packageName;
this.componentName = null;
}
void setPackageNameInternal(String packageName) {
this.packageName = packageName;
this.componentName = null;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getPackageName() {
return packageName;
}
public final boolean isDirectBootAware() {
return directBootAware;
}
public final boolean isEnabled() {
return enabled;
}
public final String getName() {
return className;
}
public final Bundle getMetaData() {
return metaData;
}
@UnsupportedAppUsage
public ComponentName getComponentName() {
if (componentName != null) {
return componentName;
}
if (className != null) {
componentName = new ComponentName(getPackageName(),
className);
}
return componentName;
}
public void setFrom(ParsedComponent other) {
this.metaData = other.metaData;
this.className = other.className;
this.icon = other.icon;
this.labelRes = other.labelRes;
this.nonLocalizedLabel = other.nonLocalizedLabel;
this.logo = other.logo;
this.banner = other.banner;
this.descriptionRes = other.descriptionRes;
this.enabled = other.enabled;
this.directBootAware = other.directBootAware;
this.flags = other.flags;
this.setPackageName(other.packageName);
this.setSplitName(other.getSplitName());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.className);
dest.writeInt(this.icon);
dest.writeInt(this.labelRes);
dest.writeCharSequence(this.nonLocalizedLabel);
dest.writeInt(this.logo);
dest.writeInt(this.banner);
dest.writeInt(this.descriptionRes);
dest.writeBoolean(this.enabled);
dest.writeBoolean(this.directBootAware);
dest.writeInt(this.flags);
dest.writeString(this.packageName);
dest.writeString(this.splitName);
ParsedIntentInfo.writeIntentsList(this.intents, dest, flags);
dest.writeBundle(this.metaData);
}
public ParsedComponent() {
}
protected ParsedComponent(Parcel in) {
// We use the boot classloader for all classes that we load.
final ClassLoader boot = Object.class.getClassLoader();
this.className = in.readString();
this.icon = in.readInt();
this.labelRes = in.readInt();
this.nonLocalizedLabel = in.readCharSequence();
this.logo = in.readInt();
this.banner = in.readInt();
this.descriptionRes = in.readInt();
this.enabled = in.readByte() != 0;
this.directBootAware = in.readByte() != 0;
this.flags = in.readInt();
this.packageName = in.readString();
this.splitName = in.readString();
this.intents = ParsedIntentInfo.createIntentsList(in);
this.metaData = in.readBundle(boot);
}
}
// TODO(b/135203078): Document this. Maybe split out ParsedComponent to be actual components
// that can have their own processes, rather than something like permission which cannot.
public static class ParsedMainComponent<IntentInfoType extends ParsedIntentInfo> extends
ParsedComponent<IntentInfoType> {
private String processName;
private String permission;
public void setProcessName(String appProcessName, String processName) {
// TODO(b/135203078): Is this even necessary anymore?
this.processName = TextUtils.safeIntern(
processName == null ? appProcessName : processName);
}
public String getProcessName() {
return processName;
}
public void setPermission(String permission) {
// Empty string must be converted to null
this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
}
public String getPermission() {
return permission;
}
@Override
public void setFrom(ParsedComponent other) {
super.setFrom(other);
if (other instanceof ParsedMainComponent) {
ParsedMainComponent otherMainComponent = (ParsedMainComponent) other;
this.setProcessName(otherMainComponent.getProcessName(),
otherMainComponent.getProcessName());
this.setPermission(otherMainComponent.getPermission());
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(this.processName);
dest.writeString(this.permission);
}
public ParsedMainComponent() {
}
protected ParsedMainComponent(Parcel in) {
super(in);
this.processName = TextUtils.safeIntern(in.readString());
this.permission = TextUtils.safeIntern(in.readString());
}
public static final Creator<ParsedMainComponent> CREATOR =
new Creator<ParsedMainComponent>() {
@Override
public ParsedMainComponent createFromParcel(Parcel source) {
return new ParsedMainComponent(source);
}
@Override
public ParsedMainComponent[] newArray(int size) {
return new ParsedMainComponent[size];
}
};
}
public static class ParsedActivity extends ParsedMainComponent<ParsedActivityIntentInfo>
implements Parcelable {
public boolean exported;
public int theme;
public int uiOptions;
public String targetActivity;
public String parentActivityName;
public String taskAffinity;
public int privateFlags;
public int launchMode;
public int documentLaunchMode;
public int maxRecents;
public int configChanges;
public int softInputMode;
public int persistableMode;
public int lockTaskLaunchMode;
public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
public int resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
public float maxAspectRatio;
public boolean hasMaxAspectRatio;
public float minAspectRatio;
public boolean hasMinAspectRatio;
public String requestedVrComponent;
public int rotationAnimation = -1;
public int colorMode;
public boolean preferMinimalPostProcessing;
public int order;
public ActivityInfo.WindowLayout windowLayout;
@Override
public void setPackageName(String packageName) {
super.setPackageName(packageName);
for (ParsedIntentInfo intent : this.intents) {
intent.packageName = packageName;
}
}
public boolean hasMaxAspectRatio() {
return hasMaxAspectRatio;
}
public boolean hasMinAspectRatio() {
return hasMinAspectRatio;
}
public void setMaxAspectRatio(int resizeMode, float maxAspectRatio) {
if (resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE
|| resizeMode == ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
// Resizeable activities can be put in any aspect ratio.
return;
}
if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
// Ignore any value lesser than 1.0.
return;
}
this.maxAspectRatio = maxAspectRatio;
hasMaxAspectRatio = true;
}
public void setMinAspectRatio(int resizeMode, float minAspectRatio) {
if (resizeMode == RESIZE_MODE_RESIZEABLE
|| resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
// Resizeable activities can be put in any aspect ratio.
return;
}
if (minAspectRatio < 1.0f && minAspectRatio != 0) {
// Ignore any value lesser than 1.0.
return;
}
this.minAspectRatio = minAspectRatio;
hasMinAspectRatio = true;
}
public void addIntent(ParsedActivityIntentInfo intent) {
this.intents.add(intent);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeBoolean(this.exported);
dest.writeInt(this.theme);
dest.writeInt(this.uiOptions);
dest.writeString(this.targetActivity);
dest.writeString(this.parentActivityName);
dest.writeString(this.taskAffinity);
dest.writeInt(this.privateFlags);
dest.writeInt(this.launchMode);
dest.writeInt(this.documentLaunchMode);
dest.writeInt(this.maxRecents);
dest.writeInt(this.configChanges);
dest.writeInt(this.softInputMode);
dest.writeInt(this.persistableMode);
dest.writeInt(this.lockTaskLaunchMode);
dest.writeInt(this.screenOrientation);
dest.writeInt(this.resizeMode);
dest.writeFloat(this.maxAspectRatio);
dest.writeBoolean(this.hasMaxAspectRatio);
dest.writeFloat(this.minAspectRatio);
dest.writeBoolean(this.hasMinAspectRatio);
dest.writeString(this.requestedVrComponent);
dest.writeInt(this.rotationAnimation);
dest.writeInt(this.colorMode);
dest.writeBoolean(this.preferMinimalPostProcessing);
dest.writeInt(this.order);
dest.writeBundle(this.metaData);
if (windowLayout != null) {
dest.writeInt(1);
dest.writeInt(windowLayout.width);
dest.writeFloat(windowLayout.widthFraction);
dest.writeInt(windowLayout.height);
dest.writeFloat(windowLayout.heightFraction);
dest.writeInt(windowLayout.gravity);
dest.writeInt(windowLayout.minWidth);
dest.writeInt(windowLayout.minHeight);
} else {
dest.writeInt(0);
}
}
public ParsedActivity() {
}
protected ParsedActivity(Parcel in) {
super(in);
this.exported = in.readByte() != 0;
this.theme = in.readInt();
this.uiOptions = in.readInt();
this.targetActivity = in.readString();
this.parentActivityName = in.readString();
this.taskAffinity = in.readString();
this.privateFlags = in.readInt();
this.launchMode = in.readInt();
this.documentLaunchMode = in.readInt();
this.maxRecents = in.readInt();
this.configChanges = in.readInt();
this.softInputMode = in.readInt();
this.persistableMode = in.readInt();
this.lockTaskLaunchMode = in.readInt();
this.screenOrientation = in.readInt();
this.resizeMode = in.readInt();
this.maxAspectRatio = in.readFloat();
this.hasMaxAspectRatio = in.readByte() != 0;
this.minAspectRatio = in.readFloat();
this.hasMinAspectRatio = in.readByte() != 0;
this.requestedVrComponent = in.readString();
this.rotationAnimation = in.readInt();
this.colorMode = in.readInt();
this.preferMinimalPostProcessing = in.readByte() != 0;
this.order = in.readInt();
this.metaData = in.readBundle();
if (in.readInt() == 1) {
windowLayout = new ActivityInfo.WindowLayout(in);
}
}
public static final Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@Override
public ParsedActivity createFromParcel(Parcel source) {
return new ParsedActivity(source);
}
@Override
public ParsedActivity[] newArray(int size) {
return new ParsedActivity[size];
}
};
}
public static class ParsedService extends ParsedMainComponent<ParsedServiceIntentInfo> {
public boolean exported;
public int flags;
public int foregroundServiceType;
public int order;
@Override
public void setPackageName(String packageName) {
super.setPackageName(packageName);
for (ParsedIntentInfo intent : this.intents) {
intent.packageName = packageName;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeBoolean(this.exported);
dest.writeBundle(this.metaData);
dest.writeInt(this.flags);
dest.writeInt(this.foregroundServiceType);
dest.writeInt(this.order);
}
public ParsedService() {
}
protected ParsedService(Parcel in) {
super(in);
this.exported = in.readByte() != 0;
this.metaData = in.readBundle();
this.flags = in.readInt();
this.foregroundServiceType = in.readInt();
this.order = in.readInt();
}
public static final Creator<ParsedService> CREATOR = new Creator<ParsedService>() {
@Override
public ParsedService createFromParcel(Parcel source) {
return new ParsedService(source);
}
@Override
public ParsedService[] newArray(int size) {
return new ParsedService[size];
}
};
}
public static class ParsedProvider extends ParsedMainComponent<ParsedProviderIntentInfo> {
protected boolean exported;
protected int flags;
protected int order;
private String authority;
protected boolean isSyncable;
private String readPermission;
private String writePermission;
protected boolean grantUriPermissions;
protected boolean forceUriPermissions;
protected boolean multiProcess;
protected int initOrder;
protected PatternMatcher[] uriPermissionPatterns;
protected PathPermission[] pathPermissions;
public ParsedProvider(ParsedProvider other) {
this.setFrom(other);
}
protected void setFrom(ParsedProvider other) {
super.setFrom(other);
this.exported = other.exported;
this.intents.clear();
if (other.intents != null) {
this.intents.addAll(other.intents);
}
this.flags = other.flags;
this.order = other.order;
this.setAuthority(other.getAuthority());
this.isSyncable = other.isSyncable;
this.setReadPermission(other.getReadPermission());
this.setWritePermission(other.getWritePermission());
this.grantUriPermissions = other.grantUriPermissions;
this.forceUriPermissions = other.forceUriPermissions;
this.multiProcess = other.multiProcess;
this.initOrder = other.initOrder;
this.uriPermissionPatterns = other.uriPermissionPatterns;
this.pathPermissions = other.pathPermissions;
}
@Override
public void setPackageName(String packageName) {
super.setPackageName(packageName);
for (ParsedIntentInfo intent : this.intents) {
intent.packageName = packageName;
}
}
public boolean isExported() {
return exported;
}
@VisibleForTesting
public void setExported(boolean exported) {
this.exported = exported;
}
public List<ParsedProviderIntentInfo> getIntents() {
return intents;
}
public int getFlags() {
return flags;
}
public int getOrder() {
return order;
}
public void setAuthority(String authority) {
this.authority = TextUtils.safeIntern(authority);
}
public String getAuthority() {
return authority;
}
public void setSyncable(boolean isSyncable) {
this.isSyncable = isSyncable;
}
public boolean isSyncable() {
return isSyncable;
}
public void setReadPermission(String readPermission) {
// Empty string must be converted to null
this.readPermission = TextUtils.isEmpty(readPermission)
? null : readPermission.intern();
}
public String getReadPermission() {
return readPermission;
}
public void setWritePermission(String writePermission) {
// Empty string must be converted to null
this.writePermission = TextUtils.isEmpty(writePermission)
? null : writePermission.intern();
}
public String getWritePermission() {
return writePermission;
}
public boolean isGrantUriPermissions() {
return grantUriPermissions;
}
public boolean isForceUriPermissions() {
return forceUriPermissions;
}
public boolean isMultiProcess() {
return multiProcess;
}
public int getInitOrder() {
return initOrder;
}
public PatternMatcher[] getUriPermissionPatterns() {
return uriPermissionPatterns;
}
public PathPermission[] getPathPermissions() {
return pathPermissions;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeBoolean(this.exported);
dest.writeInt(this.flags);
dest.writeInt(this.order);
dest.writeString(this.authority);
dest.writeBoolean(this.isSyncable);
dest.writeString(this.readPermission);
dest.writeString(this.writePermission);
dest.writeBoolean(this.grantUriPermissions);
dest.writeBoolean(this.forceUriPermissions);
dest.writeBoolean(this.multiProcess);
dest.writeInt(this.initOrder);
dest.writeTypedArray(this.uriPermissionPatterns, flags);
dest.writeTypedArray(this.pathPermissions, flags);
}
public ParsedProvider() {
}
protected ParsedProvider(Parcel in) {
super(in);
this.exported = in.readByte() != 0;
this.flags = in.readInt();
this.order = in.readInt();
this.authority = TextUtils.safeIntern(in.readString());
this.isSyncable = in.readByte() != 0;
this.readPermission = TextUtils.safeIntern(in.readString());
this.writePermission = TextUtils.safeIntern(in.readString());
this.grantUriPermissions = in.readByte() != 0;
this.forceUriPermissions = in.readByte() != 0;
this.multiProcess = in.readByte() != 0;
this.initOrder = in.readInt();
this.uriPermissionPatterns = in.createTypedArray(PatternMatcher.CREATOR);
this.pathPermissions = in.createTypedArray(PathPermission.CREATOR);
}
public static final Creator<ParsedProvider> CREATOR = new Creator<ParsedProvider>() {
@Override
public ParsedProvider createFromParcel(Parcel source) {
return new ParsedProvider(source);
}
@Override
public ParsedProvider[] newArray(int size) {
return new ParsedProvider[size];
}
};
}
/**
* A {@link android.R.styleable#AndroidManifestFeature &lt;feature&gt;} tag parsed from the
* manifest.
*/
// @DataClass verifier is broken, hence comment out for now
public static class ParsedFeature implements Parcelable {
/** Maximum length of featureId */
public static final int MAX_FEATURE_ID_LEN = 50;
/** Maximum amount of features per package */
private static final int MAX_NUM_FEATURES = 1000;
/** Id of the feature */
public final @NonNull String id;
/** User visible label fo the feature */
public final @StringRes int label;
/** Ids of previously declared features this feature inherits from */
public final @NonNull List<String> inheritFrom;
/**
* @return Is this set of features a valid combination for a single package?
*/
public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
if (features == null) {
return true;
}
ArraySet<String> featureIds = new ArraySet<>(features.size());
ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
int numFeatures = features.size();
if (numFeatures > MAX_NUM_FEATURES) {
return false;
}
for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
boolean wasAdded = featureIds.add(features.get(featureNum).id);
if (!wasAdded) {
// feature id is not unique
return false;
}
}
for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
ParsedFeature feature = features.get(featureNum);
int numInheritFrom = feature.inheritFrom.size();
for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
String inheritFrom = feature.inheritFrom.get(inheritFromNum);
if (featureIds.contains(inheritFrom)) {
// Cannot inherit from a feature that is still defined
return false;
}
boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
if (!wasAdded) {
// inheritFrom is not unique
return false;
}
}
}
return true;
}
// Code below generated by codegen v1.0.14.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/**
* Creates a new ParsedFeature.
*
* @param id
* Id of the feature
* @param label
* User visible label fo the feature (if defined as resource)
* @param inheritFrom
* Ids of previously declared features this feature inherits from
*/
@DataClass.Generated.Member
public ParsedFeature(
@NonNull String id,
@StringRes int label,
@NonNull List<String> inheritFrom) {
this.id = id;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, id);
this.label = label;
com.android.internal.util.AnnotationValidations.validate(
StringRes.class, null, label);
this.inheritFrom = inheritFrom;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, inheritFrom);
// onConstructed(); // You can define this method to get a callback
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeString(id);
dest.writeInt(label);
dest.writeStringList(inheritFrom);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
protected ParsedFeature(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
String _id = in.readString();
int _label = in.readInt();
List<String> _inheritFrom = new ArrayList<>();
in.readStringList(_inheritFrom);
this.id = _id;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, id);
this.label = _label;
com.android.internal.util.AnnotationValidations.validate(
StringRes.class, null, label);
this.inheritFrom = _inheritFrom;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, inheritFrom);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
= new Parcelable.Creator<ParsedFeature>() {
@Override
public ParsedFeature[] newArray(int size) {
return new ParsedFeature[size];
}
@Override
public ParsedFeature createFromParcel(@NonNull Parcel in) {
return new ParsedFeature(in);
}
};
/*@DataClass.Generated(
time = 1576783172965L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java",
inputSignatures = "public final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static boolean isCombinationValid(java.util.List<android.content.pm.parsing.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
*/
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}
public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> {
public String backgroundPermission;
private String group;
public int requestRes;
public int protectionLevel;
public boolean tree;
public ParsedPermissionGroup parsedPermissionGroup;
public void setName(String className) {
this.className = className;
}
public void setGroup(String group) {
this.group = TextUtils.safeIntern(group);
}
public String getGroup() {
return group;
}
public boolean isRuntime() {
return getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
}
public boolean isAppOp() {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
}
@PermissionInfo.Protection
public int getProtection() {
return protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
}
public int getProtectionFlags() {
return protectionLevel & ~PermissionInfo.PROTECTION_MASK_BASE;
}
public int calculateFootprint() {
int size = getName().length();
if (nonLocalizedLabel != null) {
size += nonLocalizedLabel.length();
}
return size;
}
public ParsedPermission() {
}
public ParsedPermission(ParsedPermission other) {
// TODO(b/135203078): Better way to copy this? Maybe refactor to the point where copy
// isn't needed.
this.className = other.className;
this.icon = other.icon;
this.labelRes = other.labelRes;
this.nonLocalizedLabel = other.nonLocalizedLabel;
this.logo = other.logo;
this.banner = other.banner;
this.descriptionRes = other.descriptionRes;
this.enabled = other.enabled;
this.directBootAware = other.directBootAware;
this.flags = other.flags;
this.setSplitName(other.getSplitName());
this.setPackageName(other.getPackageName());
this.intents.addAll(other.intents);
if (other.metaData != null) {
this.metaData = new Bundle();
this.metaData.putAll(other.metaData);
}
this.backgroundPermission = other.backgroundPermission;
this.setGroup(other.group);
this.requestRes = other.requestRes;
this.protectionLevel = other.protectionLevel;
this.tree = other.tree;
this.parsedPermissionGroup = other.parsedPermissionGroup;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(this.backgroundPermission);
dest.writeString(this.group);
dest.writeInt(this.requestRes);
dest.writeInt(this.protectionLevel);
dest.writeBoolean(this.tree);
dest.writeParcelable(this.parsedPermissionGroup, flags);
}
protected ParsedPermission(Parcel in) {
super(in);
// We use the boot classloader for all classes that we load.
final ClassLoader boot = Object.class.getClassLoader();
this.backgroundPermission = in.readString();
this.group = TextUtils.safeIntern(in.readString());
this.requestRes = in.readInt();
this.protectionLevel = in.readInt();
this.tree = in.readBoolean();
this.parsedPermissionGroup = in.readParcelable(boot);
}
public static final Creator<ParsedPermission> CREATOR = new Creator<ParsedPermission>() {
@Override
public ParsedPermission createFromParcel(Parcel source) {
return new ParsedPermission(source);
}
@Override
public ParsedPermission[] newArray(int size) {
return new ParsedPermission[size];
}
};
}
public static class ParsedPermissionGroup extends ParsedComponent<ParsedIntentInfo> {
public int requestDetailResourceId;
public int backgroundRequestResourceId;
public int backgroundRequestDetailResourceId;
public int requestRes;
public int priority;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(this.requestDetailResourceId);
dest.writeInt(this.backgroundRequestResourceId);
dest.writeInt(this.backgroundRequestDetailResourceId);
dest.writeInt(this.requestRes);
dest.writeInt(this.priority);
}
public ParsedPermissionGroup() {
}
protected ParsedPermissionGroup(Parcel in) {
super(in);
this.requestDetailResourceId = in.readInt();
this.backgroundRequestResourceId = in.readInt();
this.backgroundRequestDetailResourceId = in.readInt();
this.requestRes = in.readInt();
this.priority = in.readInt();
}
public static final Creator<ParsedPermissionGroup> CREATOR =
new Creator<ParsedPermissionGroup>() {
@Override
public ParsedPermissionGroup createFromParcel(Parcel source) {
return new ParsedPermissionGroup(source);
}
@Override
public ParsedPermissionGroup[] newArray(int size) {
return new ParsedPermissionGroup[size];
}
};
}
public static class ParsedInstrumentation extends ParsedComponent<ParsedIntentInfo> {
private String targetPackage;
private String targetProcesses;
public boolean handleProfiling;
public boolean functionalTest;
public ParsedInstrumentation() {
}
public void setTargetPackage(String targetPackage) {
this.targetPackage = TextUtils.safeIntern(targetPackage);
}
public String getTargetPackage() {
return targetPackage;
}
public void setTargetProcesses(String targetProcesses) {
this.targetProcesses = TextUtils.safeIntern(targetProcesses);
}
public String getTargetProcesses() {
return targetProcesses;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeString(this.targetPackage);
dest.writeString(this.targetProcesses);
dest.writeBoolean(this.handleProfiling);
dest.writeBoolean(this.functionalTest);
}
protected ParsedInstrumentation(Parcel in) {
super(in);
this.targetPackage = TextUtils.safeIntern(in.readString());
this.targetProcesses = TextUtils.safeIntern(in.readString());
this.handleProfiling = in.readByte() != 0;
this.functionalTest = in.readByte() != 0;
}
public static final Creator<ParsedInstrumentation> CREATOR =
new Creator<ParsedInstrumentation>() {
@Override
public ParsedInstrumentation createFromParcel(Parcel source) {
return new ParsedInstrumentation(source);
}
@Override
public ParsedInstrumentation[] newArray(int size) {
return new ParsedInstrumentation[size];
}
};
}
public static class ParsedProcess implements Parcelable {
public String name;
@Nullable
public ArraySet<String> deniedPermissions;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
final int numDenied = this.deniedPermissions != null
? this.deniedPermissions.size() : 0;
dest.writeInt(numDenied);
for (int i = 0; i < numDenied; i++) {
dest.writeString(this.deniedPermissions.valueAt(i));
}
}
public ParsedProcess() {
}
public ParsedProcess(@NonNull ParsedProcess other) {
name = other.name;
if (other.deniedPermissions != null) {
deniedPermissions = new ArraySet<>(other.deniedPermissions);
}
}
public void addStateFrom(@NonNull ParsedProcess other) {
if (other.deniedPermissions != null) {
for (int i = other.deniedPermissions.size() - 1; i >= 0; i--) {
if (deniedPermissions == null) {
deniedPermissions = new ArraySet<>(other.deniedPermissions.size());
}
deniedPermissions.add(other.deniedPermissions.valueAt(i));
}
}
}
protected ParsedProcess(Parcel in) {
this.name = TextUtils.safeIntern(in.readString());
final int numDenied = in.readInt();
if (numDenied > 0) {
this.deniedPermissions = new ArraySet<>(numDenied);
this.deniedPermissions.add(TextUtils.safeIntern(in.readString()));
}
}
public static final Creator<ParsedProcess> CREATOR =
new Creator<ParsedProcess>() {
@Override
public ParsedProcess createFromParcel(Parcel source) {
return new ParsedProcess(source);
}
@Override
public ParsedProcess[] newArray(int size) {
return new ParsedProcess[size];
}
};
}
public static ParsedActivity parseActivity(
String[] separateProcesses,
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = null;
boolean visibleToEphemeral;
boolean setExported;
int targetSdkVersion = parsingPackage.getTargetSdkVersion();
String packageName = parsingPackage.getPackageName();
String packageProcessName = parsingPackage.getProcessName();
ParsedActivity result = new ParsedActivity();
try {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
String tag = receiver ? "<receiver>" : "<activity>";
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_name, 0);
if (name == null) {
outError[0] = tag + " does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = tag + " invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestActivity_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivity_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivity_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivity_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivity_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
CharSequence pname;
if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(R.styleable.AndroidManifestActivity_process);
}
result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
packageProcessName, pname,
flags, separateProcesses, outError));
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestActivity_description, 0);
result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivity_enabled, true);
setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
if (setExported) {
result.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported,
false);
}
result.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
result.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
parsingPackage.getUiOptions());
String parentName = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_parentActivityName,
Configuration.NATIVE_CONFIG_VERSION);
if (parentName != null) {
String parentClassName = ApkParseUtils.buildClassName(packageName, parentName);
if (parentClassName == null) {
Log.e(TAG,
"Activity " + result.className
+ " specified invalid parentActivityName " +
parentName);
} else {
result.parentActivityName = parentClassName;
}
}
String str;
str = sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_permission, 0);
if (str == null) {
result.setPermission(parsingPackage.getPermission());
} else {
result.setPermission(str);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivity_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
result.taskAffinity = PackageParser.buildTaskAffinityName(
packageName,
parsingPackage.getTaskAffinity(), str, outError);
result.setSplitName(
sa.getNonConfigurationString(R.styleable.AndroidManifestActivity_splitName, 0));
result.flags = 0;
if (sa.getBoolean(
R.styleable.AndroidManifestActivity_multiprocess, false)) {
result.flags |= ActivityInfo.FLAG_MULTIPROCESS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnTaskLaunch, false)) {
result.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_clearTaskOnLaunch, false)) {
result.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_noHistory, false)) {
result.flags |= ActivityInfo.FLAG_NO_HISTORY;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysRetainTaskState, false)) {
result.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_stateNotNeeded, false)) {
result.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_excludeFromRecents, false)) {
result.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowTaskReparenting,
(parsingPackage.getFlags() & ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING)
!= 0)) {
result.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
false)) {
result.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_showOnLockScreen, false)
|| sa.getBoolean(R.styleable.AndroidManifestActivity_showForAllUsers, false)) {
result.flags |= ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_immersive, false)) {
result.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_systemUserOnly, false)) {
result.flags |= ActivityInfo.FLAG_SYSTEM_USER_ONLY;
}
boolean directBootAware;
if (!receiver) {
if (sa.getBoolean(R.styleable.AndroidManifestActivity_hardwareAccelerated,
hardwareAccelerated)) {
result.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
result.launchMode = sa.getInt(
R.styleable.AndroidManifestActivity_launchMode,
ActivityInfo.LAUNCH_MULTIPLE);
result.documentLaunchMode = sa.getInt(
R.styleable.AndroidManifestActivity_documentLaunchMode,
ActivityInfo.DOCUMENT_LAUNCH_NONE);
result.maxRecents = sa.getInt(
R.styleable.AndroidManifestActivity_maxRecents,
ActivityTaskManager.getDefaultAppRecentsLimitStatic());
result.configChanges = PackageParser.getActivityConfigChanges(
sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
result.softInputMode = sa.getInt(
R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
result.persistableMode = sa.getInteger(
R.styleable.AndroidManifestActivity_persistableMode,
ActivityInfo.PERSIST_ROOT_ONLY);
if (sa.getBoolean(R.styleable.AndroidManifestActivity_allowEmbedded, false)) {
result.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_autoRemoveFromRecents,
false)) {
result.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
false)) {
result.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resumeWhilePausing, false)) {
result.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
}
int screenOrientation = sa.getInt(
R.styleable.AndroidManifestActivity_screenOrientation,
SCREEN_ORIENTATION_UNSPECIFIED);
result.screenOrientation = screenOrientation;
int resizeMode = getActivityResizeMode(parsingPackage, sa, screenOrientation);
result.resizeMode = resizeMode;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_supportsPictureInPicture,
false)) {
result.flags |= FLAG_SUPPORTS_PICTURE_IN_PICTURE;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_alwaysFocusable, false)) {
result.flags |= FLAG_ALWAYS_FOCUSABLE;
}
if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
&& sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
== TypedValue.TYPE_FLOAT) {
result.setMaxAspectRatio(resizeMode,
sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
0 /*default*/));
}
if (sa.hasValue(R.styleable.AndroidManifestActivity_minAspectRatio)
&& sa.getType(R.styleable.AndroidManifestActivity_minAspectRatio)
== TypedValue.TYPE_FLOAT) {
result.setMinAspectRatio(resizeMode,
sa.getFloat(R.styleable.AndroidManifestActivity_minAspectRatio,
0 /*default*/));
}
result.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
result.requestedVrComponent =
sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
result.rotationAnimation =
sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation,
ROTATION_ANIMATION_UNSPECIFIED);
result.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
ActivityInfo.COLOR_MODE_DEFAULT);
result.preferMinimalPostProcessing = sa.getBoolean(
R.styleable.AndroidManifestActivity_preferMinimalPostProcessing,
ActivityInfo.MINIMAL_POST_PROCESSING_DEFAULT);
if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) {
result.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) {
result.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
}
if (sa.getBoolean(R.styleable.AndroidManifestActivity_inheritShowWhenLocked,
false)) {
result.privateFlags |= ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED;
}
} else {
result.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
result.configChanges = 0;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_singleUser, false)) {
result.flags |= ActivityInfo.FLAG_SINGLE_USER;
}
directBootAware = sa.getBoolean(
R.styleable.AndroidManifestActivity_directBootAware,
false);
}
result.directBootAware = directBootAware;
if (directBootAware) {
parsingPackage.setPartiallyDirectBootAware(true);
}
// can't make this final; we may set it later via meta-data
visibleToEphemeral = sa.getBoolean(
R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
if (visibleToEphemeral) {
result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
parsingPackage.setVisibleToInstantApps(true);
}
} finally {
if (sa != null) {
sa.recycle();
}
}
if (receiver && (parsingPackage.getPrivateFlags()
& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
// A heavy-weight application can not have receives in its main process
if (result.getProcessName().equals(packageName)) {
outError[0] = "Heavy-weight applications can not have receivers in main process";
return null;
}
}
if (outError[0] != null) {
return null;
}
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) {
ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
result.className);
if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
true /*allowGlobs*/,
true /*allowAutoVerify*/, outError)) {
return null;
}
if (intentInfo.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
} else {
result.order = Math.max(intentInfo.getOrder(), result.order);
result.addIntent(intentInfo);
}
// adjust activity flags when we implicitly expose it via a browsable filter
final int visibility = visibleToEphemeral
? IntentFilter.VISIBILITY_EXPLICIT
: !receiver && isImplicitlyExposedIntent(intentInfo)
? IntentFilter.VISIBILITY_IMPLICIT
: IntentFilter.VISIBILITY_NONE;
intentInfo.setVisibilityToInstantApp(visibility);
if (intentInfo.isVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
if (intentInfo.isImplicitlyVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
}
if (PackageParser.LOG_UNSAFE_BROADCASTS && receiver
&& (targetSdkVersion >= Build.VERSION_CODES.O)) {
for (int i = 0; i < intentInfo.countActions(); i++) {
final String action = intentInfo.getAction(i);
if (action == null || !action.startsWith("android.")) continue;
if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
+ packageName + " as requested at: "
+ parser.getPositionDescription());
}
}
}
} else if (!receiver && parser.getName().equals("preferred")) {
ParsedActivityIntentInfo intentInfo = new ParsedActivityIntentInfo(packageName,
result.className);
if (!parseIntentInfo(intentInfo, parsingPackage, res, parser,
false /*allowGlobs*/,
false /*allowAutoVerify*/, outError)) {
return null;
}
// adjust activity flags when we implicitly expose it via a browsable filter
final int visibility = visibleToEphemeral
? IntentFilter.VISIBILITY_EXPLICIT
: !receiver && isImplicitlyExposedIntent(intentInfo)
? IntentFilter.VISIBILITY_IMPLICIT
: IntentFilter.VISIBILITY_NONE;
intentInfo.setVisibilityToInstantApp(visibility);
if (intentInfo.isVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
if (intentInfo.isImplicitlyVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
}
if (intentInfo.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
} else {
parsingPackage.addPreferredActivityFilter(intentInfo);
}
} else if (parser.getName().equals("meta-data")) {
if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
result.metaData,
outError)) == null) {
return null;
}
} else if (!receiver && parser.getName().equals("layout")) {
result.windowLayout = parseLayout(res, parser);
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + parsingPackage.getBaseCodePath() + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+ " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+ " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
if (receiver) {
outError[0] = "Bad element under <receiver>: " + parser.getName();
} else {
outError[0] = "Bad element under <activity>: " + parser.getName();
}
return null;
}
}
}
if (!setExported) {
result.exported = result.intents.size() > 0;
}
return result;
}
public static boolean isImplicitlyExposedIntent(ParsedIntentInfo intentInfo) {
return intentInfo.hasCategory(Intent.CATEGORY_BROWSABLE)
|| intentInfo.hasAction(Intent.ACTION_SEND)
|| intentInfo.hasAction(Intent.ACTION_SENDTO)
|| intentInfo.hasAction(Intent.ACTION_SEND_MULTIPLE);
}
public static int getActivityResizeMode(
ParsingPackage parsingPackage,
TypedArray sa,
int screenOrientation
) {
int privateFlags = parsingPackage.getPrivateFlags();
final boolean appExplicitDefault = (privateFlags
& (ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
| ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE)) != 0;
if (sa.hasValue(R.styleable.AndroidManifestActivity_resizeableActivity)
|| appExplicitDefault) {
// Activity or app explicitly set if it is resizeable or not;
final boolean appResizeable = (privateFlags
& ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE) != 0;
if (sa.getBoolean(R.styleable.AndroidManifestActivity_resizeableActivity,
appResizeable)) {
return ActivityInfo.RESIZE_MODE_RESIZEABLE;
} else {
return ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
}
}
if ((privateFlags
& ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
!= 0) {
// The activity or app didn't explicitly set the resizing option, however we want to
// make it resize due to the sdk version it is targeting.
return ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
}
// resize preference isn't set and target sdk version doesn't support resizing apps by
// default. For the app to be resizeable if it isn't fixed orientation or immersive.
if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
} else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
} else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
return ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
} else {
return ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
}
}
public static ParsedService parseService(
String[] separateProcesses,
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser, int flags, String[] outError
) throws XmlPullParserException, IOException {
TypedArray sa = null;
boolean visibleToEphemeral;
boolean setExported;
String packageName = parsingPackage.getPackageName();
String packageProcessName = parsingPackage.getProcessName();
ParsedService result = new ParsedService();
try {
sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestService);
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestService_name, 0);
if (name == null) {
outError[0] = "<service> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<service> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestService_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestService_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestService_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestService_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestService_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
CharSequence pname;
if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(R.styleable.AndroidManifestService_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(R.styleable.AndroidManifestService_process);
}
result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
packageProcessName, pname,
flags, separateProcesses, outError));
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestService_description, 0);
result.enabled = sa.getBoolean(R.styleable.AndroidManifestService_enabled, true);
setExported = sa.hasValue(
R.styleable.AndroidManifestService_exported);
if (setExported) {
result.exported = sa.getBoolean(
R.styleable.AndroidManifestService_exported, false);
}
String str = sa.getNonConfigurationString(
R.styleable.AndroidManifestService_permission, 0);
if (str == null) {
result.setPermission(parsingPackage.getPermission());
} else {
result.setPermission(str);
}
result.setSplitName(
sa.getNonConfigurationString(R.styleable.AndroidManifestService_splitName, 0));
result.foregroundServiceType = sa.getInt(
R.styleable.AndroidManifestService_foregroundServiceType,
ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE);
result.flags = 0;
if (sa.getBoolean(
R.styleable.AndroidManifestService_stopWithTask,
false)) {
result.flags |= ServiceInfo.FLAG_STOP_WITH_TASK;
}
if (sa.getBoolean(
R.styleable.AndroidManifestService_isolatedProcess,
false)) {
result.flags |= ServiceInfo.FLAG_ISOLATED_PROCESS;
}
if (sa.getBoolean(
R.styleable.AndroidManifestService_externalService,
false)) {
result.flags |= ServiceInfo.FLAG_EXTERNAL_SERVICE;
}
if (sa.getBoolean(
R.styleable.AndroidManifestService_useAppZygote,
false)) {
result.flags |= ServiceInfo.FLAG_USE_APP_ZYGOTE;
}
if (sa.getBoolean(
R.styleable.AndroidManifestService_singleUser,
false)) {
result.flags |= ServiceInfo.FLAG_SINGLE_USER;
}
result.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestService_directBootAware,
false);
if (result.directBootAware) {
parsingPackage.setPartiallyDirectBootAware(true);
}
visibleToEphemeral = sa.getBoolean(
R.styleable.AndroidManifestService_visibleToInstantApps, false);
if (visibleToEphemeral) {
result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
parsingPackage.setVisibleToInstantApps(true);
}
} finally {
if (sa != null) {
sa.recycle();
}
}
if (parsingPackage.cantSaveState()) {
// A heavy-weight application can not have services in its main process
// We can do direct compare because we intern all strings.
if (Objects.equals(result.getProcessName(), parsingPackage.getPackageName())) {
outError[0] = "Heavy-weight applications can not have services in main process";
return null;
}
}
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) {
ParsedServiceIntentInfo intent = new ParsedServiceIntentInfo(packageName,
result.className);
if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
false /*allowAutoVerify*/,
outError)) {
return null;
}
if (visibleToEphemeral) {
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
result.flags |= ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
result.order = Math.max(intent.getOrder(), result.order);
result.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
result.metaData,
outError)) == null) {
return null;
}
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <service>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <service>: " + parser.getName();
return null;
}
}
}
if (!setExported) {
result.exported = result.intents.size() > 0;
}
return result;
}
public static ParsedProvider parseProvider(
String[] separateProcesses,
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = null;
String cpname;
boolean visibleToEphemeral;
int targetSdkVersion = parsingPackage.getTargetSdkVersion();
String packageName = parsingPackage.getPackageName();
String packageProcessName = parsingPackage.getProcessName();
ParsedProvider result = new ParsedProvider();
try {
sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestProvider);
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_name, 0);
if (name == null) {
outError[0] = "<provider> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<provider> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestProvider_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestProvider_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestProvider_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestProvider_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestProvider_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
CharSequence pname;
if (parsingPackage.getTargetSdkVersion() >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(R.styleable.AndroidManifestProvider_process);
}
result.setProcessName(packageProcessName, PackageParser.buildProcessName(packageName,
packageProcessName, pname,
flags, separateProcesses, outError));
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestProvider_description, 0);
result.enabled = sa.getBoolean(R.styleable.AndroidManifestProvider_enabled, true);
boolean providerExportedDefault = false;
if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
// For compatibility, applications targeting API level 16 or lower
// should have their content providers exported by default, unless they
// specify otherwise.
providerExportedDefault = true;
}
result.exported = sa.getBoolean(
R.styleable.AndroidManifestProvider_exported,
providerExportedDefault);
cpname = sa.getNonConfigurationString(
R.styleable.AndroidManifestProvider_authorities, 0);
result.isSyncable = sa.getBoolean(
R.styleable.AndroidManifestProvider_syncable,
false);
String permission = sa.getNonConfigurationString(
R.styleable.AndroidManifestProvider_permission, 0);
String str = sa.getNonConfigurationString(
R.styleable.AndroidManifestProvider_readPermission, 0);
if (str == null) {
str = permission;
}
if (str == null) {
result.setReadPermission(parsingPackage.getPermission());
} else {
result.setReadPermission(str);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestProvider_writePermission, 0);
if (str == null) {
str = permission;
}
if (str == null) {
result.setWritePermission(parsingPackage.getPermission());
} else {
result.setWritePermission(str);
}
result.grantUriPermissions = sa.getBoolean(
R.styleable.AndroidManifestProvider_grantUriPermissions,
false);
result.forceUriPermissions = sa.getBoolean(
R.styleable.AndroidManifestProvider_forceUriPermissions,
false);
result.multiProcess = sa.getBoolean(
R.styleable.AndroidManifestProvider_multiprocess,
false);
result.initOrder = sa.getInt(
R.styleable.AndroidManifestProvider_initOrder,
0);
result.setSplitName(
sa.getNonConfigurationString(R.styleable.AndroidManifestProvider_splitName, 0));
result.flags = 0;
if (sa.getBoolean(
R.styleable.AndroidManifestProvider_singleUser,
false)) {
result.flags |= ProviderInfo.FLAG_SINGLE_USER;
}
result.directBootAware = sa.getBoolean(
R.styleable.AndroidManifestProvider_directBootAware,
false);
if (result.directBootAware) {
parsingPackage.setPartiallyDirectBootAware(true);
}
visibleToEphemeral =
sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
if (visibleToEphemeral) {
result.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
parsingPackage.setVisibleToInstantApps(true);
}
} finally {
if (sa != null) {
sa.recycle();
}
}
if ((parsingPackage.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
!= 0) {
// A heavy-weight application can not have providers in its main process
if (result.getProcessName().equals(packageName)) {
outError[0] = "Heavy-weight applications can not have providers in main process";
return null;
}
}
if (cpname == null) {
outError[0] = "<provider> does not include authorities attribute";
return null;
}
if (cpname.length() <= 0) {
outError[0] = "<provider> has empty authorities attribute";
return null;
}
result.setAuthority(cpname);
if (!parseProviderTags(parsingPackage, res, parser, visibleToEphemeral, result, outError)) {
return null;
}
return result;
}
public static ParsedQueriesIntentInfo parsedParsedQueriesIntentInfo(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
ParsedQueriesIntentInfo intentInfo = new ParsedQueriesIntentInfo(
parsingPackage.getPackageName(),
null
);
if (!parseIntentInfo(
intentInfo,
parsingPackage,
res,
parser,
true /*allowGlobs*/,
true /*allowAutoVerify*/,
outError
)) {
return null;
}
return intentInfo;
}
private static boolean parseProviderTags(
ParsingPackage parsingPackage,
Resources res, XmlResourceParser parser,
boolean visibleToEphemeral, ParsedProvider outInfo, String[] outError)
throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) {
ParsedProviderIntentInfo intent = new ParsedProviderIntentInfo(
parsingPackage.getPackageName(), outInfo.className);
if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
false /*allowAutoVerify*/,
outError)) {
return false;
}
if (visibleToEphemeral) {
intent.setVisibilityToInstantApp(IntentFilter.VISIBILITY_EXPLICIT);
outInfo.flags |= ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
outInfo.order = Math.max(intent.getOrder(), outInfo.order);
outInfo.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
Bundle metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
outInfo.metaData, outError);
if (metaData == null) {
return false;
} else {
outInfo.metaData = metaData;
}
} else if (parser.getName().equals("grant-uri-permission")) {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestGrantUriPermission);
PatternMatcher pa = null;
String str = sa.getNonConfigurationString(
R.styleable.AndroidManifestGrantUriPermission_path, 0);
if (str != null) {
pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0);
if (str != null) {
pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0);
if (str != null) {
pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
sa.recycle();
if (pa != null) {
if (outInfo.uriPermissionPatterns == null) {
outInfo.uriPermissionPatterns = new PatternMatcher[1];
outInfo.uriPermissionPatterns[0] = pa;
} else {
final int N = outInfo.uriPermissionPatterns.length;
PatternMatcher[] newp = new PatternMatcher[N + 1];
System.arraycopy(outInfo.uriPermissionPatterns, 0, newp, 0, N);
newp[N] = pa;
outInfo.uriPermissionPatterns = newp;
}
outInfo.grantUriPermissions = true;
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <path-permission>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath()
+ " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
return false;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (parser.getName().equals("path-permission")) {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestPathPermission);
PathPermission pa = null;
String permission = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_permission, 0);
String readPermission = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_readPermission, 0);
if (readPermission == null) {
readPermission = permission;
}
String writePermission = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_writePermission, 0);
if (writePermission == null) {
writePermission = permission;
}
boolean havePerm = false;
if (readPermission != null) {
readPermission = readPermission.intern();
havePerm = true;
}
if (writePermission != null) {
writePermission = writePermission.intern();
havePerm = true;
}
if (!havePerm) {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "No readPermission or writePermssion for <path-permission>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath()
+ " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "No readPermission or writePermssion for <path-permission>";
return false;
}
}
String path = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_path, 0);
if (path != null) {
pa = new PathPermission(path,
PatternMatcher.PATTERN_LITERAL, readPermission, writePermission);
}
path = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_pathPrefix, 0);
if (path != null) {
pa = new PathPermission(path,
PatternMatcher.PATTERN_PREFIX, readPermission, writePermission);
}
path = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_pathPattern, 0);
if (path != null) {
pa = new PathPermission(path,
PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission);
}
path = sa.getNonConfigurationString(
R.styleable.AndroidManifestPathPermission_pathAdvancedPattern, 0);
if (path != null) {
pa = new PathPermission(path,
PatternMatcher.PATTERN_ADVANCED_GLOB, readPermission, writePermission);
}
sa.recycle();
if (pa != null) {
if (outInfo.pathPermissions == null) {
outInfo.pathPermissions = new PathPermission[1];
outInfo.pathPermissions[0] = pa;
} else {
final int N = outInfo.pathPermissions.length;
PathPermission[] newp = new PathPermission[N + 1];
System.arraycopy(outInfo.pathPermissions, 0, newp, 0, N);
newp[N] = pa;
outInfo.pathPermissions = newp;
}
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "No path, pathPrefix, or pathPattern for <path-permission>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath()
+ " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
outError[0] = "No path, pathPrefix, or pathPattern for <path-permission>";
return false;
}
XmlUtils.skipCurrentTag(parser);
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <provider>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <provider>: " + parser.getName();
return false;
}
}
}
return true;
}
public static ParsedActivity parseActivityAlias(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestActivityAlias);
String targetActivity = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivityAlias_targetActivity,
Configuration.NATIVE_CONFIG_VERSION);
if (targetActivity == null) {
outError[0] = "<activity-alias> does not specify android:targetActivity";
sa.recycle();
return null;
}
String packageName = parsingPackage.getPackageName();
targetActivity = ApkParseUtils.buildClassName(packageName, targetActivity);
if (targetActivity == null) {
outError[0] = "Empty class name in package " + packageName;
sa.recycle();
return null;
}
ParsedActivity target = null;
List<ParsedActivity> activities = parsingPackage.getActivities();
final int NA = activities.size();
for (int i = 0; i < NA; i++) {
ParsedActivity t = activities.get(i);
if (targetActivity.equals(t.className)) {
target = t;
break;
}
}
if (target == null) {
outError[0] = "<activity-alias> target activity " + targetActivity
+ " not found in manifest with activities = " + parsingPackage.getActivities()
+ ", parsedActivities = " + activities;
sa.recycle();
return null;
}
ParsedActivity result = new ParsedActivity();
result.setPackageNameInternal(target.getPackageName());
result.targetActivity = targetActivity;
result.configChanges = target.configChanges;
result.flags = target.flags;
result.privateFlags = target.privateFlags;
result.icon = target.icon;
result.logo = target.logo;
result.banner = target.banner;
result.labelRes = target.labelRes;
result.nonLocalizedLabel = target.nonLocalizedLabel;
result.launchMode = target.launchMode;
result.lockTaskLaunchMode = target.lockTaskLaunchMode;
result.descriptionRes = target.descriptionRes;
result.screenOrientation = target.screenOrientation;
result.taskAffinity = target.taskAffinity;
result.theme = target.theme;
result.softInputMode = target.softInputMode;
result.uiOptions = target.uiOptions;
result.parentActivityName = target.parentActivityName;
result.maxRecents = target.maxRecents;
result.windowLayout = target.windowLayout;
result.resizeMode = target.resizeMode;
result.maxAspectRatio = target.maxAspectRatio;
result.hasMaxAspectRatio = target.hasMaxAspectRatio;
result.minAspectRatio = target.minAspectRatio;
result.hasMinAspectRatio = target.hasMinAspectRatio;
result.requestedVrComponent = target.requestedVrComponent;
result.directBootAware = target.directBootAware;
result.setProcessName(parsingPackage.getAppInfoProcessName(), target.getProcessName());
// Not all attributes from the target ParsedActivity are copied to the alias.
// Careful when adding an attribute and determine whether or not it should be copied.
// result.enabled = target.enabled;
// result.exported = target.exported;
// result.permission = target.permission;
// result.splitName = target.splitName;
// result.documentLaunchMode = target.documentLaunchMode;
// result.persistableMode = target.persistableMode;
// result.rotationAnimation = target.rotationAnimation;
// result.colorMode = target.colorMode;
// result.intents.addAll(target.intents);
// result.order = target.order;
// result.metaData = target.metaData;
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestActivityAlias_name,
0);
if (name == null) {
outError[0] = "<activity-alias> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<activity-alias> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestActivityAlias_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestActivityAlias_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestActivityAlias_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestActivityAlias_description, 0);
result.enabled = sa.getBoolean(R.styleable.AndroidManifestActivityAlias_enabled, true);
final boolean setExported = sa.hasValue(
R.styleable.AndroidManifestActivityAlias_exported);
if (setExported) {
result.exported = sa.getBoolean(
R.styleable.AndroidManifestActivityAlias_exported, false);
}
String str;
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivityAlias_permission, 0);
if (str != null) {
result.setPermission(str);
}
String parentName = sa.getNonConfigurationString(
R.styleable.AndroidManifestActivityAlias_parentActivityName,
Configuration.NATIVE_CONFIG_VERSION);
if (parentName != null) {
String parentClassName = ApkParseUtils.buildClassName(result.getPackageName(),
parentName);
if (parentClassName == null) {
Log.e(TAG, "Activity alias " + result.className +
" specified invalid parentActivityName " + parentName);
outError[0] = null;
} else {
result.parentActivityName = parentClassName;
}
}
// TODO add visibleToInstantApps attribute to activity alias
final boolean visibleToEphemeral =
((result.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0);
sa.recycle();
if (outError[0] != null) {
return null;
}
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("intent-filter")) {
ParsedActivityIntentInfo intent = new ParsedActivityIntentInfo(packageName,
result.className);
if (!parseIntentInfo(intent, parsingPackage, res, parser, true /*allowGlobs*/,
true /*allowAutoVerify*/, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
} else {
result.order = Math.max(intent.getOrder(), result.order);
result.addIntent(intent);
}
// adjust activity flags when we implicitly expose it via a browsable filter
final int visibility = visibleToEphemeral
? IntentFilter.VISIBILITY_EXPLICIT
: isImplicitlyExposedIntent(intent)
? IntentFilter.VISIBILITY_IMPLICIT
: IntentFilter.VISIBILITY_NONE;
intent.setVisibilityToInstantApp(visibility);
if (intent.isVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
}
if (intent.isImplicitlyVisibleToInstantApp()) {
result.flags |= ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP;
}
} else if (tagName.equals("meta-data")) {
if ((result.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
result.metaData,
outError)) == null) {
return null;
}
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <activity-alias>: " + tagName
+ " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <activity-alias>: " + tagName;
return null;
}
}
}
if (!setExported) {
result.exported = result.intents.size() > 0;
}
return result;
}
public static ParsedFeature parseFeature(
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
String featureId;
int label;
List<String> inheritFrom = null;
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
if (sa == null) {
outError[0] = "<feature> could not be parsed";
return null;
}
try {
featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
0);
if (featureId == null) {
outError[0] = "<featureId> does not specify android:featureId";
return null;
}
if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
outError[0] = "<featureId> is too long. Max length is "
+ ParsedFeature.MAX_FEATURE_ID_LEN;
return null;
}
label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
if (label == Resources.ID_NULL) {
outError[0] = "<featureId> does not specify android:label";
return null;
}
} finally {
sa.recycle();
}
int type;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("inherit-from")) {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
if (sa == null) {
outError[0] = "<inherit-from> could not be parsed";
return null;
}
try {
String inheritFromId = sa.getNonConfigurationString(
R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
if (inheritFrom == null) {
inheritFrom = new ArrayList<>();
}
inheritFrom.add(inheritFromId);
} finally {
sa.recycle();
}
} else {
outError[0] = "Bad element under <feature>: " + tagName;
return null;
}
}
if (inheritFrom == null) {
inheritFrom = Collections.emptyList();
} else {
((ArrayList) inheritFrom).trimToSize();
}
return new ParsedFeature(featureId, label, inheritFrom);
}
public static ParsedPermission parsePermission(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = null;
String packageName = parsingPackage.getPackageName();
ParsedPermission result = new ParsedPermission();
try {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermission);
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestPermission_name,
0);
if (name == null) {
outError[0] = "<permission> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<permission> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestPermission_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermission_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermission_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermission_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermission_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestPermission_description, 0);
if (sa.hasValue(
R.styleable.AndroidManifestPermission_backgroundPermission)) {
if ("android".equals(packageName)) {
result.backgroundPermission = sa.getNonResourceString(
R.styleable
.AndroidManifestPermission_backgroundPermission);
} else {
Slog.w(TAG, packageName + " defines a background permission. Only the "
+ "'android' package can do that.");
}
}
// Note: don't allow this value to be a reference to a resource
// that may change.
result.setGroup(sa.getNonResourceString(
R.styleable.AndroidManifestPermission_permissionGroup));
result.requestRes = sa.getResourceId(
R.styleable.AndroidManifestPermission_request, 0);
result.protectionLevel = sa.getInt(
R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
result.flags = sa.getInt(
R.styleable.AndroidManifestPermission_permissionFlags, 0);
// For now only platform runtime permissions can be restricted
if (!result.isRuntime() || !"android".equals(result.getPackageName())) {
result.flags &= ~PermissionInfo.FLAG_HARD_RESTRICTED;
result.flags &= ~PermissionInfo.FLAG_SOFT_RESTRICTED;
} else {
// The platform does not get to specify conflicting permissions
if ((result.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0
&& (result.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0) {
throw new IllegalStateException("Permission cannot be both soft and hard"
+ " restricted: " + result.getName());
}
}
} finally {
if (sa != null) {
sa.recycle();
}
}
if (result.protectionLevel == -1) {
outError[0] = "<permission> does not specify protectionLevel";
return null;
}
result.protectionLevel = PermissionInfo.fixProtectionLevel(result.protectionLevel);
if (result.getProtectionFlags() != 0) {
if ((result.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
&& (result.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
== 0
&& (result.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
outError[0] = "<permission> protectionLevel specifies a non-instant flag but is "
+ "not based on signature type";
return null;
}
}
boolean success = parseAllMetaData(parsingPackage, res, parser,
"<permission>", result, outError);
if (!success || outError[0] != null) {
return null;
}
return result;
}
public static ParsedPermission parsePermissionTree(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = null;
String packageName = parsingPackage.getPackageName();
ParsedPermission result = new ParsedPermission();
try {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionTree);
String name = sa.getNonConfigurationString(
R.styleable.AndroidManifestPermissionTree_name, 0);
if (name == null) {
outError[0] = "<permission-tree> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<permission-tree> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestPermissionTree_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionTree_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionTree_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
} finally {
if (sa != null) {
sa.recycle();
}
}
int index = result.getName().indexOf('.');
if (index > 0) {
index = result.getName().indexOf('.', index + 1);
}
if (index < 0) {
outError[0] =
"<permission-tree> name has less than three segments: " + result.getName();
return null;
}
result.descriptionRes = 0;
result.requestRes = 0;
result.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
result.tree = true;
boolean success = parseAllMetaData(parsingPackage, res, parser,
"<permission-tree>", result, outError);
if (!success || outError[0] != null) {
return null;
}
return result;
}
public static ParsedPermissionGroup parsePermissionGroup(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = null;
String packageName = parsingPackage.getPackageName();
ParsedPermissionGroup result = new ParsedPermissionGroup();
try {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestPermissionGroup);
String name = sa.getNonConfigurationString(
R.styleable.AndroidManifestPermissionGroup_name, 0);
if (name == null) {
outError[0] = "<permission> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<permission> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestPermissionGroup_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestPermissionGroup_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestPermissionGroup_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
result.descriptionRes = sa.getResourceId(
R.styleable.AndroidManifestPermissionGroup_description, 0);
result.requestDetailResourceId = sa.getResourceId(
R.styleable.AndroidManifestPermissionGroup_requestDetail, 0);
result.backgroundRequestResourceId = sa.getResourceId(
R.styleable.AndroidManifestPermissionGroup_backgroundRequest,
0);
result.backgroundRequestDetailResourceId = sa.getResourceId(
R.styleable
.AndroidManifestPermissionGroup_backgroundRequestDetail, 0);
result.requestRes = sa.getResourceId(
R.styleable.AndroidManifestPermissionGroup_request, 0);
result.flags = sa.getInt(
R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags,
0);
result.priority = sa.getInt(
R.styleable.AndroidManifestPermissionGroup_priority, 0);
} finally {
if (sa != null) {
sa.recycle();
}
}
boolean success = parseAllMetaData(parsingPackage, res, parser,
"<permission-group>", result, outError);
if (!success || outError[0] != null) {
return null;
}
return result;
}
public static ParsedInstrumentation parseInstrumentation(
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = null;
String packageName = parsingPackage.getPackageName();
ParsedInstrumentation result = new ParsedInstrumentation();
try {
sa = res.obtainAttributes(parser, R.styleable.AndroidManifestInstrumentation);
// TODO(b/135203078): Re-share all of the configuration for this. ParseComponentArgs was
// un-used for this, but can be adjusted and re-added to share all the initial result
// parsing for icon/logo/name/etc in all of these parse methods.
String name = sa.getNonConfigurationString(
R.styleable.AndroidManifestInstrumentation_name, 0);
if (name == null) {
outError[0] = "<instrumentation> does not specify android:name";
return null;
} else {
String className = ApkParseUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
outError[0] = "<instrumentation> invalid android:name";
return null;
} else if (className == null) {
outError[0] = "Empty class name in package " + packageName;
return null;
}
result.className = className;
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestInstrumentation_roundIcon, 0) : 0;
if (roundIconVal != 0) {
result.icon = roundIconVal;
result.nonLocalizedLabel = null;
} else {
int iconVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_icon, 0);
if (iconVal != 0) {
result.icon = iconVal;
result.nonLocalizedLabel = null;
}
}
int logoVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_logo, 0);
if (logoVal != 0) {
result.logo = logoVal;
}
int bannerVal = sa.getResourceId(R.styleable.AndroidManifestInstrumentation_banner, 0);
if (bannerVal != 0) {
result.banner = bannerVal;
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestInstrumentation_label);
if (v != null && (result.labelRes = v.resourceId) == 0) {
result.nonLocalizedLabel = v.coerceToString();
}
result.setPackageNameInternal(packageName);
String str;
// Note: don't allow this value to be a reference to a resource
// that may change.
str = sa.getNonResourceString(R.styleable.AndroidManifestInstrumentation_targetPackage);
result.setTargetPackage(str);
str = sa.getNonResourceString(
R.styleable.AndroidManifestInstrumentation_targetProcesses);
result.setTargetProcesses(str);
result.handleProfiling = sa.getBoolean(
R.styleable.AndroidManifestInstrumentation_handleProfiling, false);
result.functionalTest = sa.getBoolean(
R.styleable.AndroidManifestInstrumentation_functionalTest, false);
} finally {
if (sa != null) {
sa.recycle();
}
}
boolean success = parseAllMetaData(parsingPackage, res, parser,
"<instrumentation>", result, outError);
if (!success || outError[0] != null) {
return null;
}
return result;
}
private static @Nullable ArraySet<String> parseDenyPermission(
ArraySet<String> perms,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestDenyPermission);
if (sa == null) {
outError[0] = "<deny-permission> could not be parsed";
return null;
}
try {
String perm = sa.getNonConfigurationString(
R.styleable.AndroidManifestDenyPermission_name,0);
if (perm != null && perm.equals(android.Manifest.permission.INTERNET)) {
if (perms == null) {
perms = new ArraySet<>();
}
perms.add(perm);
}
} finally {
sa.recycle();
}
XmlUtils.skipCurrentTag(parser);
return perms;
}
private static ArraySet<String> parseAllowPermission(
ArraySet<String> perms,
Resources res,
XmlResourceParser parser,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestAllowPermission);
if (sa == null) {
outError[0] = "<allow-permission> could not be parsed";
return null;
}
try {
String perm = sa.getNonConfigurationString(
R.styleable.AndroidManifestAllowPermission_name,0);
if (perm != null && perm.equals(android.Manifest.permission.INTERNET)
&& perms != null) {
perms.remove(perm);
if (perms.size() <= 0) {
perms = null;
}
}
} finally {
sa.recycle();
}
XmlUtils.skipCurrentTag(parser);
return perms;
}
public static ParsedProcess parseProcess(
ArraySet<String> perms,
String[] separateProcesses,
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
int flags,
String[] outError
) throws IOException, XmlPullParserException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestProcess);
if (sa == null) {
outError[0] = "<process> could not be parsed";
return null;
}
ParsedProcess proc = new ParsedProcess();
if (perms != null) {
proc.deniedPermissions = new ArraySet(perms);
}
try {
proc.name = sa.getNonConfigurationString(
R.styleable.AndroidManifestProcess_process,0);
proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
null, proc.name, flags, separateProcesses, outError);
if (proc.name == null || proc.name.length() <= 0) {
outError[0] = "<process> does not specify android:process";
return null;
}
proc.name = PackageParser.buildProcessName(parsingPackage.getPackageName(),
parsingPackage.getPackageName(), proc.name,
flags, separateProcesses, outError);
if (outError[0] != null) {
return null;
}
} finally {
sa.recycle();
}
int type;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("deny-permission")) {
proc.deniedPermissions = parseDenyPermission(proc.deniedPermissions, res, parser,
outError);
if (outError[0] != null) {
return null;
}
} else if (tagName.equals("allow-permission")) {
proc.deniedPermissions = parseAllowPermission(proc.deniedPermissions, res, parser,
outError);
if (outError[0] != null) {
return null;
}
} else {
Slog.w(TAG, "Unknown element under <process>: " + tagName
+ " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
return proc;
}
public static ArrayMap<String, ParsedProcess> parseProcesses(
String[] separateProcesses,
ParsingPackage parsingPackage,
Resources res,
XmlResourceParser parser,
int flags,
String[] outError
) throws IOException, XmlPullParserException {
ArraySet<String> deniedPerms = null;
ArrayMap<String, ParsedProcess> processes = new ArrayMap<>();
int type;
final int innerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("deny-permission")) {
deniedPerms = parseDenyPermission(deniedPerms, res, parser, outError);
if (outError[0] != null) {
return null;
}
} else if (tagName.equals("allow-permission")) {
deniedPerms = parseAllowPermission(deniedPerms, res, parser, outError);
if (outError[0] != null) {
return null;
}
} else if (tagName.equals("process")) {
ParsedProcess proc = parseProcess(deniedPerms, separateProcesses, parsingPackage,
res, parser, flags, outError);
if (outError[0] != null) {
return null;
}
if (processes.get(proc.name) != null) {
outError[0] = "<process> specified existing name '" + proc.name + "'";
return null;
}
processes.put(proc.name, proc);
} else {
Slog.w(TAG, "Unknown element under <processes>: " + tagName
+ " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
return processes;
}
public static ActivityInfo.WindowLayout parseLayout(Resources res, AttributeSet attrs) {
TypedArray sw = res.obtainAttributes(attrs,
R.styleable.AndroidManifestLayout);
int width = -1;
float widthFraction = -1f;
int height = -1;
float heightFraction = -1f;
final int widthType = sw.getType(
R.styleable.AndroidManifestLayout_defaultWidth);
if (widthType == TypedValue.TYPE_FRACTION) {
widthFraction = sw.getFraction(
R.styleable.AndroidManifestLayout_defaultWidth,
1, 1, -1);
} else if (widthType == TypedValue.TYPE_DIMENSION) {
width = sw.getDimensionPixelSize(
R.styleable.AndroidManifestLayout_defaultWidth,
-1);
}
final int heightType = sw.getType(
R.styleable.AndroidManifestLayout_defaultHeight);
if (heightType == TypedValue.TYPE_FRACTION) {
heightFraction = sw.getFraction(
R.styleable.AndroidManifestLayout_defaultHeight,
1, 1, -1);
} else if (heightType == TypedValue.TYPE_DIMENSION) {
height = sw.getDimensionPixelSize(
R.styleable.AndroidManifestLayout_defaultHeight,
-1);
}
int gravity = sw.getInt(
R.styleable.AndroidManifestLayout_gravity,
Gravity.CENTER);
int minWidth = sw.getDimensionPixelSize(
R.styleable.AndroidManifestLayout_minWidth,
-1);
int minHeight = sw.getDimensionPixelSize(
R.styleable.AndroidManifestLayout_minHeight,
-1);
sw.recycle();
return new ActivityInfo.WindowLayout(width, widthFraction,
height, heightFraction, gravity, minWidth, minHeight);
}
public static boolean parseIntentInfo(
ParsedIntentInfo intentInfo,
ParsingPackage parsingPackage,
Resources res, XmlResourceParser parser, boolean allowGlobs,
boolean allowAutoVerify, String[] outError
) throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestIntentFilter);
int priority = sa.getInt(
R.styleable.AndroidManifestIntentFilter_priority, 0);
intentInfo.setPriority(priority);
int order = sa.getInt(
R.styleable.AndroidManifestIntentFilter_order, 0);
intentInfo.setOrder(order);
TypedValue v = sa.peekValue(
R.styleable.AndroidManifestIntentFilter_label);
if (v != null && (intentInfo.labelRes = v.resourceId) == 0) {
intentInfo.nonLocalizedLabel = v.coerceToString();
}
int roundIconVal = PackageParser.sUseRoundIcon ? sa.getResourceId(
R.styleable.AndroidManifestIntentFilter_roundIcon, 0) : 0;
if (roundIconVal != 0) {
intentInfo.icon = roundIconVal;
} else {
intentInfo.icon = sa.getResourceId(
R.styleable.AndroidManifestIntentFilter_icon, 0);
}
if (allowAutoVerify) {
intentInfo.setAutoVerify(sa.getBoolean(
R.styleable.AndroidManifestIntentFilter_autoVerify,
false));
}
sa.recycle();
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String nodeName = parser.getName();
if (nodeName.equals("action")) {
String value = parser.getAttributeValue(
PackageParser.ANDROID_RESOURCES, "name");
if (TextUtils.isEmpty(value)) {
outError[0] = "No value supplied for <android:name>";
return false;
}
XmlUtils.skipCurrentTag(parser);
intentInfo.addAction(value);
} else if (nodeName.equals("category")) {
String value = parser.getAttributeValue(
PackageParser.ANDROID_RESOURCES, "name");
if (TextUtils.isEmpty(value)) {
outError[0] = "No value supplied for <android:name>";
return false;
}
XmlUtils.skipCurrentTag(parser);
intentInfo.addCategory(value);
} else if (nodeName.equals("data")) {
sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestData);
String str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_mimeType, 0);
if (str != null) {
try {
intentInfo.addRawDataType(str);
} catch (IntentFilter.MalformedMimeTypeException e) {
outError[0] = e.toString();
sa.recycle();
return false;
}
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_scheme, 0);
if (str != null) {
intentInfo.addDataScheme(str);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_ssp, 0);
if (str != null) {
intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_LITERAL);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_sspPrefix, 0);
if (str != null) {
intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_PREFIX);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_sspPattern, 0);
if (str != null) {
if (!allowGlobs) {
outError[0] = "sspPattern not allowed here; ssp must be literal";
return false;
}
intentInfo.addDataSchemeSpecificPart(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
String host = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_host, 0);
String port = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_port, 0);
if (host != null) {
intentInfo.addDataAuthority(host, port);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_path, 0);
if (str != null) {
intentInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_pathPrefix, 0);
if (str != null) {
intentInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_pathPattern, 0);
if (str != null) {
if (!allowGlobs) {
outError[0] = "pathPattern not allowed here; path must be literal";
return false;
}
intentInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
str = sa.getNonConfigurationString(
R.styleable.AndroidManifestData_pathAdvancedPattern, 0);
if (str != null) {
if (!allowGlobs) {
outError[0] = "pathAdvancedPattern not allowed here; path must be literal";
return false;
}
intentInfo.addDataPath(str, PatternMatcher.PATTERN_ADVANCED_GLOB);
}
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <intent-filter>: "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
} else {
outError[0] = "Bad element under <intent-filter>: " + parser.getName();
return false;
}
}
intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
if (PackageParser.DEBUG_PARSER) {
final StringBuilder cats = new StringBuilder("Intent d=");
cats.append(intentInfo.hasDefault);
cats.append(", cat=");
final Iterator<String> it = intentInfo.categoriesIterator();
if (it != null) {
while (it.hasNext()) {
cats.append(' ');
cats.append(it.next());
}
}
Slog.d(TAG, cats.toString());
}
return true;
}
private static boolean parseAllMetaData(
ParsingPackage parsingPackage,
Resources res, XmlResourceParser parser, String tag,
ParsedComponent outInfo,
String[] outError
) throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("meta-data")) {
if ((outInfo.metaData = ApkParseUtils.parseMetaData(parsingPackage, res, parser,
outInfo.metaData, outError)) == null) {
return false;
}
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG, "Unknown element under " + tag + ": "
+ parser.getName() + " at " + parsingPackage.getBaseCodePath() + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under " + tag + ": " + parser.getName();
}
}
}
return true;
}
public static boolean isImplicitlyExposedIntent(IntentFilter intent) {
return intent.hasCategory(Intent.CATEGORY_BROWSABLE)
|| intent.hasAction(Intent.ACTION_SEND)
|| intent.hasAction(Intent.ACTION_SENDTO)
|| intent.hasAction(Intent.ACTION_SEND_MULTIPLE);
}
}