blob: e1887bc64452584a1a569fcfb82e289b8c5b4835 [file] [log] [blame]
/*
* Copyright (C) 2007 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;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.TypedValue;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
* Package archive parsing
*
* {@hide}
*/
public class PackageParser {
private static final boolean DEBUG_JAR = false;
private static final boolean DEBUG_PARSER = false;
private static final boolean DEBUG_BACKUP = false;
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
/** @hide */
public static class NewPermissionInfo {
public final String name;
public final int sdkVersion;
public final int fileVersion;
public NewPermissionInfo(String name, int sdkVersion, int fileVersion) {
this.name = name;
this.sdkVersion = sdkVersion;
this.fileVersion = fileVersion;
}
}
/** @hide */
public static class SplitPermissionInfo {
public final String rootPerm;
public final String[] newPerms;
public final int targetSdk;
public SplitPermissionInfo(String rootPerm, String[] newPerms, int targetSdk) {
this.rootPerm = rootPerm;
this.newPerms = newPerms;
this.targetSdk = targetSdk;
}
}
/**
* List of new permissions that have been added since 1.0.
* NOTE: These must be declared in SDK version order, with permissions
* added to older SDKs appearing before those added to newer SDKs.
* If sdkVersion is 0, then this is not a permission that we want to
* automatically add to older apps, but we do want to allow it to be
* granted during a platform update.
* @hide
*/
public static final PackageParser.NewPermissionInfo NEW_PERMISSIONS[] =
new PackageParser.NewPermissionInfo[] {
new PackageParser.NewPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.os.Build.VERSION_CODES.DONUT, 0),
new PackageParser.NewPermissionInfo(android.Manifest.permission.READ_PHONE_STATE,
android.os.Build.VERSION_CODES.DONUT, 0)
};
/**
* List of permissions that have been split into more granular or dependent
* permissions.
* @hide
*/
public static final PackageParser.SplitPermissionInfo SPLIT_PERMISSIONS[] =
new PackageParser.SplitPermissionInfo[] {
// READ_EXTERNAL_STORAGE is always required when an app requests
// WRITE_EXTERNAL_STORAGE, because we can't have an app that has
// write access without read access. The hack here with the target
// target SDK version ensures that this grant is always done.
new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
new String[] { android.Manifest.permission.READ_EXTERNAL_STORAGE },
android.os.Build.VERSION_CODES.CUR_DEVELOPMENT+1),
new PackageParser.SplitPermissionInfo(android.Manifest.permission.READ_CONTACTS,
new String[] { android.Manifest.permission.READ_CALL_LOG },
android.os.Build.VERSION_CODES.JELLY_BEAN),
new PackageParser.SplitPermissionInfo(android.Manifest.permission.WRITE_CONTACTS,
new String[] { android.Manifest.permission.WRITE_CALL_LOG },
android.os.Build.VERSION_CODES.JELLY_BEAN)
};
private String mArchiveSourcePath;
private String[] mSeparateProcesses;
private boolean mOnlyCoreApps;
private static final int SDK_VERSION = Build.VERSION.SDK_INT;
private static final String SDK_CODENAME = "REL".equals(Build.VERSION.CODENAME)
? null : Build.VERSION.CODENAME;
private int mParseError = PackageManager.INSTALL_SUCCEEDED;
private static final Object mSync = new Object();
private static WeakReference<byte[]> mReadBuffer;
private static boolean sCompatibilityModeEnabled = true;
private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
static class ParsePackageItemArgs {
final Package owner;
final String[] outError;
final int nameRes;
final int labelRes;
final int iconRes;
final int logoRes;
String tag;
TypedArray sa;
ParsePackageItemArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _logoRes) {
owner = _owner;
outError = _outError;
nameRes = _nameRes;
labelRes = _labelRes;
iconRes = _iconRes;
logoRes = _logoRes;
}
}
static class ParseComponentArgs extends ParsePackageItemArgs {
final String[] sepProcesses;
final int processRes;
final int descriptionRes;
final int enabledRes;
int flags;
ParseComponentArgs(Package _owner, String[] _outError,
int _nameRes, int _labelRes, int _iconRes, int _logoRes,
String[] _sepProcesses, int _processRes,
int _descriptionRes, int _enabledRes) {
super(_owner, _outError, _nameRes, _labelRes, _iconRes, _logoRes);
sepProcesses = _sepProcesses;
processRes = _processRes;
descriptionRes = _descriptionRes;
enabledRes = _enabledRes;
}
}
/* Light weight package info.
* @hide
*/
public static class PackageLite {
public final String packageName;
public final int versionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public PackageLite(String packageName, int versionCode,
int installLocation, List<VerifierInfo> verifiers) {
this.packageName = packageName;
this.versionCode = versionCode;
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
}
}
private ParsePackageItemArgs mParseInstrumentationArgs;
private ParseComponentArgs mParseActivityArgs;
private ParseComponentArgs mParseActivityAliasArgs;
private ParseComponentArgs mParseServiceArgs;
private ParseComponentArgs mParseProviderArgs;
/** If set to true, we will only allow package files that exactly match
* the DTD. Otherwise, we try to get as much from the package as we
* can without failing. This should normally be set to false, to
* support extensions to the DTD in future versions. */
private static final boolean RIGID_PARSER = false;
private static final String TAG = "PackageParser";
public PackageParser(String archiveSourcePath) {
mArchiveSourcePath = archiveSourcePath;
}
public void setSeparateProcesses(String[] procs) {
mSeparateProcesses = procs;
}
public void setOnlyCoreApps(boolean onlyCoreApps) {
mOnlyCoreApps = onlyCoreApps;
}
private static final boolean isPackageFilename(String name) {
return name.endsWith(".apk");
}
/*
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions) {
PackageUserState state = new PackageUserState();
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
}
*/
/**
* Generate and return the {@link PackageInfo} for a parsed package.
*
* @param p the parsed package.
* @param flags indicating which optional information is included.
*/
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions, PackageUserState state) {
return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
grantedPermissions, state, UserHandle.getCallingUserId());
}
private static boolean checkUseInstalled(int flags, PackageUserState state) {
return state.installed || ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0);
}
public static PackageInfo generatePackageInfo(PackageParser.Package p,
int gids[], int flags, long firstInstallTime, long lastUpdateTime,
HashSet<String> grantedPermissions, PackageUserState state, int userId) {
if (!checkUseInstalled(flags, state)) {
return null;
}
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
pi.gids = gids;
}
if ((flags&PackageManager.GET_CONFIGURATIONS) != 0) {
int N = p.configPreferences.size();
if (N > 0) {
pi.configPreferences = new ConfigurationInfo[N];
p.configPreferences.toArray(pi.configPreferences);
}
N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
if (N > 0) {
pi.reqFeatures = new FeatureInfo[N];
p.reqFeatures.toArray(pi.reqFeatures);
}
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
int N = p.activities.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities = new ActivityInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.activities.get(i).info.enabled) num++;
}
pi.activities = new ActivityInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Activity activity = p.activities.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.activities[j++] = generateActivityInfo(p.activities.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_RECEIVERS) != 0) {
int N = p.receivers.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers = new ActivityInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.receivers.get(i).info.enabled) num++;
}
pi.receivers = new ActivityInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Activity activity = p.receivers.get(i);
if (activity.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.receivers[j++] = generateActivityInfo(p.receivers.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_SERVICES) != 0) {
int N = p.services.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.services = new ServiceInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.services.get(i).info.enabled) num++;
}
pi.services = new ServiceInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Service service = p.services.get(i);
if (service.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.services[j++] = generateServiceInfo(p.services.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_PROVIDERS) != 0) {
int N = p.providers.size();
if (N > 0) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.providers = new ProviderInfo[N];
} else {
int num = 0;
for (int i=0; i<N; i++) {
if (p.providers.get(i).info.enabled) num++;
}
pi.providers = new ProviderInfo[num];
}
for (int i=0, j=0; i<N; i++) {
final Provider provider = p.providers.get(i);
if (provider.info.enabled
|| (flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
pi.providers[j++] = generateProviderInfo(p.providers.get(i), flags,
state, userId);
}
}
}
}
if ((flags&PackageManager.GET_INSTRUMENTATION) != 0) {
int N = p.instrumentation.size();
if (N > 0) {
pi.instrumentation = new InstrumentationInfo[N];
for (int i=0; i<N; i++) {
pi.instrumentation[i] = generateInstrumentationInfo(
p.instrumentation.get(i), flags);
}
}
}
if ((flags&PackageManager.GET_PERMISSIONS) != 0) {
int N = p.permissions.size();
if (N > 0) {
pi.permissions = new PermissionInfo[N];
for (int i=0; i<N; i++) {
pi.permissions[i] = generatePermissionInfo(p.permissions.get(i), flags);
}
}
N = p.requestedPermissions.size();
if (N > 0) {
pi.requestedPermissions = new String[N];
pi.requestedPermissionsFlags = new int[N];
for (int i=0; i<N; i++) {
final String perm = p.requestedPermissions.get(i);
pi.requestedPermissions[i] = perm;
if (p.requestedPermissionsRequired.get(i)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
}
if (grantedPermissions != null && grantedPermissions.contains(perm)) {
pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
}
}
}
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
int N = (p.mSignatures != null) ? p.mSignatures.length : 0;
if (N > 0) {
pi.signatures = new Signature[N];
System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
}
}
return pi;
}
private Certificate[] loadCertificates(JarFile jarFile, JarEntry je,
byte[] readBuffer) {
try {
// We must read the stream for the JarEntry to retrieve
// its certificates.
InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
while (is.read(readBuffer, 0, readBuffer.length) != -1) {
// not using
}
is.close();
return je != null ? je.getCertificates() : null;
} catch (IOException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in "
+ jarFile.getName(), e);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception reading " + je.getName() + " in "
+ jarFile.getName(), e);
}
return null;
}
public final static int PARSE_IS_SYSTEM = 1<<0;
public final static int PARSE_CHATTY = 1<<1;
public final static int PARSE_MUST_BE_APK = 1<<2;
public final static int PARSE_IGNORE_PROCESSES = 1<<3;
public final static int PARSE_FORWARD_LOCK = 1<<4;
public final static int PARSE_ON_SDCARD = 1<<5;
public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
public int getParseError() {
return mParseError;
}
public Package parsePackage(File sourceFile, String destCodePath,
DisplayMetrics metrics, int flags) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = sourceFile.getPath();
if (!sourceFile.isFile()) {
Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
return null;
}
if (!isPackageFilename(sourceFile.getName())
&& (flags&PARSE_MUST_BE_APK) != 0) {
if ((flags&PARSE_IS_SYSTEM) == 0) {
// We expect to have non-.apk files in the system dir,
// so don't warn about them.
Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
}
mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
return null;
}
if (DEBUG_JAR)
Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
XmlResourceParser parser = null;
AssetManager assmgr = null;
Resources res = null;
boolean assetError = true;
try {
assmgr = new AssetManager();
int cookie = assmgr.addAssetPath(mArchiveSourcePath);
if (cookie != 0) {
res = new Resources(assmgr, metrics, null);
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
assetError = false;
} else {
Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
}
} catch (Exception e) {
Slog.w(TAG, "Unable to read AndroidManifest.xml of "
+ mArchiveSourcePath, e);
}
if (assetError) {
if (assmgr != null) assmgr.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
return null;
}
String[] errorText = new String[1];
Package pkg = null;
Exception errorException = null;
try {
// XXXX todo: need to figure out correct configuration.
pkg = parsePackage(res, parser, flags, errorText);
} catch (Exception e) {
errorException = e;
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
}
if (pkg == null) {
// If we are only parsing core apps, then a null with INSTALL_SUCCEEDED
// just means to skip this app so don't make a fuss about it.
if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
if (errorException != null) {
Slog.w(TAG, mArchiveSourcePath, errorException);
} else {
Slog.w(TAG, mArchiveSourcePath + " (at "
+ parser.getPositionDescription()
+ "): " + errorText[0]);
}
if (mParseError == PackageManager.INSTALL_SUCCEEDED) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
}
}
parser.close();
assmgr.close();
return null;
}
parser.close();
assmgr.close();
// Set code and resource paths
pkg.mPath = destCodePath;
pkg.mScanPath = mArchiveSourcePath;
//pkg.applicationInfo.sourceDir = destCodePath;
//pkg.applicationInfo.publicSourceDir = destRes;
pkg.mSignatures = null;
return pkg;
}
public boolean collectCertificates(Package pkg, int flags) {
pkg.mSignatures = null;
WeakReference<byte[]> readBufferRef;
byte[] readBuffer = null;
synchronized (mSync) {
readBufferRef = mReadBuffer;
if (readBufferRef != null) {
mReadBuffer = null;
readBuffer = readBufferRef.get();
}
if (readBuffer == null) {
readBuffer = new byte[8192];
readBufferRef = new WeakReference<byte[]>(readBuffer);
}
}
try {
JarFile jarFile = new JarFile(mArchiveSourcePath);
Certificate[] certs = null;
if ((flags&PARSE_IS_SYSTEM) != 0) {
// If this package comes from the system image, then we
// can trust it... we'll just use the AndroidManifest.xml
// to retrieve its signatures, not validating all of the
// files.
JarEntry jarEntry = jarFile.getJarEntry(ANDROID_MANIFEST_FILENAME);
certs = loadCertificates(jarFile, jarEntry, readBuffer);
if (certs == null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no certificates at entry "
+ jarEntry.getName() + "; ignoring!");
jarFile.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
return false;
}
if (DEBUG_JAR) {
Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
+ " certs=" + (certs != null ? certs.length : 0));
if (certs != null) {
final int N = certs.length;
for (int i=0; i<N; i++) {
Slog.i(TAG, " Public key: "
+ certs[i].getPublicKey().getEncoded()
+ " " + certs[i].getPublicKey());
}
}
}
} else {
Enumeration<JarEntry> entries = jarFile.entries();
final Manifest manifest = jarFile.getManifest();
while (entries.hasMoreElements()) {
final JarEntry je = entries.nextElement();
if (je.isDirectory()) continue;
final String name = je.getName();
if (name.startsWith("META-INF/"))
continue;
if (ANDROID_MANIFEST_FILENAME.equals(name)) {
final Attributes attributes = manifest.getAttributes(name);
pkg.manifestDigest = ManifestDigest.fromAttributes(attributes);
}
final Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer);
if (DEBUG_JAR) {
Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
+ ": certs=" + certs + " ("
+ (certs != null ? certs.length : 0) + ")");
}
if (localCerts == null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no certificates at entry "
+ je.getName() + "; ignoring!");
jarFile.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
return false;
} else if (certs == null) {
certs = localCerts;
} else {
// Ensure all certificates match.
for (int i=0; i<certs.length; i++) {
boolean found = false;
for (int j=0; j<localCerts.length; j++) {
if (certs[i] != null &&
certs[i].equals(localCerts[j])) {
found = true;
break;
}
}
if (!found || certs.length != localCerts.length) {
Slog.e(TAG, "Package " + pkg.packageName
+ " has mismatched certificates at entry "
+ je.getName() + "; ignoring!");
jarFile.close();
mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return false;
}
}
}
}
}
jarFile.close();
synchronized (mSync) {
mReadBuffer = readBufferRef;
}
if (certs != null && certs.length > 0) {
final int N = certs.length;
pkg.mSignatures = new Signature[certs.length];
for (int i=0; i<N; i++) {
pkg.mSignatures[i] = new Signature(
certs[i].getEncoded());
}
} else {
Slog.e(TAG, "Package " + pkg.packageName
+ " has no certificates; ignoring!");
mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
return false;
}
} catch (CertificateEncodingException e) {
Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
return false;
} catch (IOException e) {
Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
return false;
} catch (RuntimeException e) {
Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
return false;
}
return true;
}
/*
* Utility method that retrieves just the package name and install
* location from the apk location at the given file path.
* @param packageFilePath file location of the apk
* @param flags Special parse flags
* @return PackageLite object with package information or null on failure.
*/
public static PackageLite parsePackageLite(String packageFilePath, int flags) {
AssetManager assmgr = null;
final XmlResourceParser parser;
final Resources res;
try {
assmgr = new AssetManager();
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
int cookie = assmgr.addAssetPath(packageFilePath);
if (cookie == 0) {
return null;
}
final DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
res = new Resources(assmgr, metrics, null);
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
} catch (Exception e) {
if (assmgr != null) assmgr.close();
Slog.w(TAG, "Unable to read AndroidManifest.xml of "
+ packageFilePath, e);
return null;
}
final AttributeSet attrs = parser;
final String errors[] = new String[1];
PackageLite packageLite = null;
try {
packageLite = parsePackageLite(res, parser, attrs, flags, errors);
} catch (IOException e) {
Slog.w(TAG, packageFilePath, e);
} catch (XmlPullParserException e) {
Slog.w(TAG, packageFilePath, e);
} finally {
if (parser != null) parser.close();
if (assmgr != null) assmgr.close();
}
if (packageLite == null) {
Slog.e(TAG, "parsePackageLite error: " + errors[0]);
return null;
}
return packageLite;
}
private static String validateName(String name, boolean requiresSeparator) {
final int N = name.length();
boolean hasSep = false;
boolean front = true;
for (int i=0; i<N; i++) {
final char c = name.charAt(i);
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
front = false;
continue;
}
if (!front) {
if ((c >= '0' && c <= '9') || c == '_') {
continue;
}
}
if (c == '.') {
hasSep = true;
front = true;
continue;
}
return "bad character '" + c + "'";
}
return hasSep || !requiresSeparator
? null : "must have at least one '.' separator";
}
private static String parsePackageName(XmlPullParser parser,
AttributeSet attrs, int flags, String[] outError)
throws IOException, XmlPullParserException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
outError[0] = "No start tag found";
return null;
}
if (DEBUG_PARSER)
Slog.v(TAG, "Root element name: '" + parser.getName() + "'");
if (!parser.getName().equals("manifest")) {
outError[0] = "No <manifest> tag";
return null;
}
String pkgName = attrs.getAttributeValue(null, "package");
if (pkgName == null || pkgName.length() == 0) {
outError[0] = "<manifest> does not specify package";
return null;
}
String nameError = validateName(pkgName, true);
if (nameError != null && !"android".equals(pkgName)) {
outError[0] = "<manifest> specifies bad package name \""
+ pkgName + "\": " + nameError;
return null;
}
return pkgName.intern();
}
private static PackageLite parsePackageLite(Resources res, XmlPullParser parser,
AttributeSet attrs, int flags, String[] outError) throws IOException,
XmlPullParserException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
outError[0] = "No start tag found";
return null;
}
if (DEBUG_PARSER)
Slog.v(TAG, "Root element name: '" + parser.getName() + "'");
if (!parser.getName().equals("manifest")) {
outError[0] = "No <manifest> tag";
return null;
}
String pkgName = attrs.getAttributeValue(null, "package");
if (pkgName == null || pkgName.length() == 0) {
outError[0] = "<manifest> does not specify package";
return null;
}
String nameError = validateName(pkgName, true);
if (nameError != null && !"android".equals(pkgName)) {
outError[0] = "<manifest> specifies bad package name \""
+ pkgName + "\": " + nameError;
return null;
}
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
int numFound = 0;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
numFound++;
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
numFound++;
}
if (numFound >= 2) {
break;
}
}
// Only search the tree when the tag is directly below <manifest>
final int searchDepth = parser.getDepth() + 1;
final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError);
if (verifier != null) {
verifiers.add(verifier);
}
}
}
return new PackageLite(pkgName.intern(), versionCode, installLocation, verifiers);
}
/**
* Temporary.
*/
static public Signature stringToSignature(String str) {
final int N = str.length();
byte[] sig = new byte[N];
for (int i=0; i<N; i++) {
sig[i] = (byte)str.charAt(i);
}
return new Signature(sig);
}
private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
AttributeSet attrs = parser;
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
String pkgName = parsePackageName(parser, attrs, flags, outError);
if (pkgName == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
int type;
if (mOnlyCoreApps) {
boolean core = attrs.getAttributeBooleanValue(null, "coreApp", false);
if (!core) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
return null;
}
}
final Package pkg = new Package(pkgName);
boolean foundApp = false;
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
String nameError = validateName(str, true);
if (nameError != null && !"android".equals(pkgName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
+ str + "\": " + nameError;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
return null;
}
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
sa.recycle();
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installLocation;
/* Set the global "forward lock" flag */
if ((flags & PARSE_FORWARD_LOCK) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FORWARD_LOCK;
}
/* Set the global "on SD card" flag */
if ((flags & PARSE_ON_SDCARD) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
}
// Resource boolean are -1, so 1 means we don't know the value.
int supportsSmallScreens = 1;
int supportsNormalScreens = 1;
int supportsLargeScreens = 1;
int supportsXLargeScreens = 1;
int resizeable = 1;
int anyDensity = 1;
int outerDepth = parser.getDepth();
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("application")) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = "<manifest> has more than one <application>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
XmlUtils.skipCurrentTag(parser);
continue;
}
}
foundApp = true;
if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
} else if (tagName.equals("permission-group")) {
if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission")) {
if (parsePermission(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission-tree")) {
if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("uses-permission")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesPermission);
// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
/* Not supporting optional permissions yet.
boolean required = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
*/
sa.recycle();
if (name != null && !pkg.requestedPermissions.contains(name)) {
pkg.requestedPermissions.add(name.intern());
pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-configuration")) {
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
cPref.reqTouchScreen = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
Configuration.TOUCHSCREEN_UNDEFINED);
cPref.reqKeyboardType = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
Configuration.KEYBOARD_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
}
cPref.reqNavigation = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
Configuration.NAVIGATION_UNDEFINED);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
false)) {
cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
}
sa.recycle();
pkg.configPreferences.add(cPref);
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-feature")) {
FeatureInfo fi = new FeatureInfo();
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesFeature);
// Note: don't allow this value to be a reference to a resource
// that may change.
fi.name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
if (fi.name == null) {
fi.reqGlEsVersion = sa.getInt(
com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
FeatureInfo.GL_ES_VERSION_UNDEFINED);
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
true)) {
fi.flags |= FeatureInfo.FLAG_REQUIRED;
}
sa.recycle();
if (pkg.reqFeatures == null) {
pkg.reqFeatures = new ArrayList<FeatureInfo>();
}
pkg.reqFeatures.add(fi);
if (fi.name == null) {
ConfigurationInfo cPref = new ConfigurationInfo();
cPref.reqGlEsVersion = fi.reqGlEsVersion;
pkg.configPreferences.add(cPref);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-sdk")) {
if (SDK_VERSION > 0) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesSdk);
int minVers = 0;
String minCode = null;
int targetVers = 0;
String targetCode = null;
TypedValue val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
targetVers = minVers = val.data;
}
}
val = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
if (val != null) {
if (val.type == TypedValue.TYPE_STRING && val.string != null) {
targetCode = minCode = val.string.toString();
} else {
// If it's not a string, it's an integer.
targetVers = val.data;
}
}
sa.recycle();
if (minCode != null) {
if (!minCode.equals(SDK_CODENAME)) {
if (SDK_CODENAME != null) {
outError[0] = "Requires development platform " + minCode
+ " (current platform is " + SDK_CODENAME + ")";
} else {
outError[0] = "Requires development platform " + minCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
} else if (minVers > SDK_VERSION) {
outError[0] = "Requires newer sdk version #" + minVers
+ " (current version is #" + SDK_VERSION + ")";
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
if (targetCode != null) {
if (!targetCode.equals(SDK_CODENAME)) {
if (SDK_CODENAME != null) {
outError[0] = "Requires development platform " + targetCode
+ " (current platform is " + SDK_CODENAME + ")";
} else {
outError[0] = "Requires development platform " + targetCode
+ " but this is a release platform.";
}
mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
return null;
}
// If the code matches, it definitely targets this SDK.
pkg.applicationInfo.targetSdkVersion
= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
} else {
pkg.applicationInfo.targetSdkVersion = targetVers;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("supports-screens")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestSupportsScreens);
pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
0);
pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
0);
pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
0);
// This is a trick to get a boolean and still able to detect
// if a value was actually set.
supportsSmallScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
supportsSmallScreens);
supportsNormalScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
supportsNormalScreens);
supportsLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
supportsLargeScreens);
supportsXLargeScreens = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
supportsXLargeScreens);
resizeable = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
resizeable);
anyDensity = sa.getInteger(
com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
anyDensity);
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("protected-broadcast")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
sa.recycle();
if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
if (pkg.protectedBroadcasts == null) {
pkg.protectedBroadcasts = new ArrayList<String>();
}
if (!pkg.protectedBroadcasts.contains(name)) {
pkg.protectedBroadcasts.add(name.intern());
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("instrumentation")) {
if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("original-package")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String orig =sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
if (!pkg.packageName.equals(orig)) {
if (pkg.mOriginalPackages == null) {
pkg.mOriginalPackages = new ArrayList<String>();
pkg.mRealPackage = pkg.packageName;
}
pkg.mOriginalPackages.add(orig);
}
sa.recycle();
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("adopt-permissions")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestOriginalPackage);
String name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
sa.recycle();
if (name != null) {
if (pkg.mAdoptPermissions == null) {
pkg.mAdoptPermissions = new ArrayList<String>();
}
pkg.mAdoptPermissions.add(name);
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-gl-texture")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals("compatible-screens")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (tagName.equals("eat-comment")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
continue;
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
}
if (!foundApp && pkg.instrumentation.size() == 0) {
outError[0] = "<manifest> does not contain an <application> or <instrumentation>";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_EMPTY;
}
final int NP = PackageParser.NEW_PERMISSIONS.length;
StringBuilder implicitPerms = null;
for (int ip=0; ip<NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
if (pkg.applicationInfo.targetSdkVersion >= npi.sdkVersion) {
break;
}
if (!pkg.requestedPermissions.contains(npi.name)) {
if (implicitPerms == null) {
implicitPerms = new StringBuilder(128);
implicitPerms.append(pkg.packageName);
implicitPerms.append(": compat added ");
} else {
implicitPerms.append(' ');
}
implicitPerms.append(npi.name);
pkg.requestedPermissions.add(npi.name);
pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
if (implicitPerms != null) {
Slog.i(TAG, implicitPerms.toString());
}
final int NS = PackageParser.SPLIT_PERMISSIONS.length;
for (int is=0; is<NS; is++) {
final PackageParser.SplitPermissionInfo spi
= PackageParser.SPLIT_PERMISSIONS[is];
if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk
|| !pkg.requestedPermissions.contains(spi.rootPerm)) {
continue;
}
for (int in=0; in<spi.newPerms.length; in++) {
final String perm = spi.newPerms[in];
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
pkg.requestedPermissionsRequired.add(Boolean.TRUE);
}
}
}
if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
}
if (supportsNormalScreens != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
}
if (supportsLargeScreens < 0 || (supportsLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
}
if (supportsXLargeScreens < 0 || (supportsXLargeScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS;
}
if (resizeable < 0 || (resizeable > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
}
if (anyDensity < 0 || (anyDensity > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
}
return pkg;
}
private static String buildClassName(String pkg, CharSequence clsSeq,
String[] outError) {
if (clsSeq == null || clsSeq.length() <= 0) {
outError[0] = "Empty class name in package " + pkg;
return null;
}
String cls = clsSeq.toString();
char c = cls.charAt(0);
if (c == '.') {
return (pkg + cls).intern();
}
if (cls.indexOf('.') < 0) {
StringBuilder b = new StringBuilder(pkg);
b.append('.');
b.append(cls);
return b.toString().intern();
}
if (c >= 'a' && c <= 'z') {
return cls.intern();
}
outError[0] = "Bad class name " + cls + " in package " + pkg;
return null;
}
private static String buildCompoundName(String pkg,
CharSequence procSeq, String type, String[] outError) {
String proc = procSeq.toString();
char c = proc.charAt(0);
if (pkg != null && c == ':') {
if (proc.length() < 2) {
outError[0] = "Bad " + type + " name " + proc + " in package " + pkg
+ ": must be at least two characters";
return null;
}
String subName = proc.substring(1);
String nameError = validateName(subName, false);
if (nameError != null) {
outError[0] = "Invalid " + type + " name " + proc + " in package "
+ pkg + ": " + nameError;
return null;
}
return (pkg + proc).intern();
}
String nameError = validateName(proc, true);
if (nameError != null && !"system".equals(proc)) {
outError[0] = "Invalid " + type + " name " + proc + " in package "
+ pkg + ": " + nameError;
return null;
}
return proc.intern();
}
private static String buildProcessName(String pkg, String defProc,
CharSequence procSeq, int flags, String[] separateProcesses,
String[] outError) {
if ((flags&PARSE_IGNORE_PROCESSES) != 0 && !"system".equals(procSeq)) {
return defProc != null ? defProc : pkg;
}
if (separateProcesses != null) {
for (int i=separateProcesses.length-1; i>=0; i--) {
String sp = separateProcesses[i];
if (sp.equals(pkg) || sp.equals(defProc) || sp.equals(procSeq)) {
return pkg;
}
}
}
if (procSeq == null || procSeq.length() <= 0) {
return defProc;
}
return buildCompoundName(pkg, procSeq, "process", outError);
}
private static String buildTaskAffinityName(String pkg, String defProc,
CharSequence procSeq, String[] outError) {
if (procSeq == null) {
return defProc;
}
if (procSeq.length() <= 0) {
return null;
}
return buildCompoundName(pkg, procSeq, "taskAffinity", outError);
}
private PermissionGroup parsePermissionGroup(Package owner, int flags, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
PermissionGroup perm = new PermissionGroup(owner);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestPermissionGroup);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission-group>", sa,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_name,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_label,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_icon,
com.android.internal.R.styleable.AndroidManifestPermissionGroup_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_description,
0);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_permissionGroupFlags, 0);
perm.info.priority = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermissionGroup_priority, 0);
if (perm.info.priority > 0 && (flags&PARSE_IS_SYSTEM) == 0) {
perm.info.priority = 0;
}
sa.recycle();
if (!parseAllMetaData(res, parser, attrs, "<permission-group>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
owner.permissionGroups.add(perm);
return perm;
}
private Permission parsePermission(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestPermission);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission>", sa,
com.android.internal.R.styleable.AndroidManifestPermission_name,
com.android.internal.R.styleable.AndroidManifestPermission_label,
com.android.internal.R.styleable.AndroidManifestPermission_icon,
com.android.internal.R.styleable.AndroidManifestPermission_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
// Note: don't allow this value to be a reference to a resource
// that may change.
perm.info.group = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup);
if (perm.info.group != null) {
perm.info.group = perm.info.group.intern();
}
perm.info.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestPermission_description,
0);
perm.info.protectionLevel = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_protectionLevel,
PermissionInfo.PROTECTION_NORMAL);
perm.info.flags = sa.getInt(
com.android.internal.R.styleable.AndroidManifestPermission_permissionFlags, 0);
sa.recycle();
if (perm.info.protectionLevel == -1) {
outError[0] = "<permission> does not specify protectionLevel";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
PermissionInfo.PROTECTION_SIGNATURE) {
outError[0] = "<permission> protectionLevel specifies a flag but is "
+ "not based on signature type";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
}
if (!parseAllMetaData(res, parser, attrs, "<permission>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
owner.permissions.add(perm);
return perm;
}
private Permission parsePermissionTree(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
Permission perm = new Permission(owner);
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestPermissionTree);
if (!parsePackageItemInfo(owner, perm.info, outError,
"<permission-tree>", sa,
com.android.internal.R.styleable.AndroidManifestPermissionTree_name,
com.android.internal.R.styleable.AndroidManifestPermissionTree_label,
com.android.internal.R.styleable.AndroidManifestPermissionTree_icon,
com.android.internal.R.styleable.AndroidManifestPermissionTree_logo)) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
sa.recycle();
int index = perm.info.name.indexOf('.');
if (index > 0) {
index = perm.info.name.indexOf('.', index+1);
}
if (index < 0) {
outError[0] = "<permission-tree> name has less than three segments: "
+ perm.info.name;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
perm.info.descriptionRes = 0;
perm.info.protectionLevel = PermissionInfo.PROTECTION_NORMAL;
perm.tree = true;
if (!parseAllMetaData(res, parser, attrs, "<permission-tree>", perm,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
owner.permissions.add(perm);
return perm;
}
private Instrumentation parseInstrumentation(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestInstrumentation);
if (mParseInstrumentationArgs == null) {
mParseInstrumentationArgs = new ParsePackageItemArgs(owner, outError,
com.android.internal.R.styleable.AndroidManifestInstrumentation_name,
com.android.internal.R.styleable.AndroidManifestInstrumentation_label,
com.android.internal.R.styleable.AndroidManifestInstrumentation_icon,
com.android.internal.R.styleable.AndroidManifestInstrumentation_logo);
mParseInstrumentationArgs.tag = "<instrumentation>";
}
mParseInstrumentationArgs.sa = sa;
Instrumentation a = new Instrumentation(mParseInstrumentationArgs,
new InstrumentationInfo());
if (outError[0] != null) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
String str;
// Note: don't allow this value to be a reference to a resource
// that may change.
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage);
a.info.targetPackage = str != null ? str.intern() : null;
a.info.handleProfiling = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestInstrumentation_handleProfiling,
false);
a.info.functionalTest = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestInstrumentation_functionalTest,
false);
sa.recycle();
if (a.info.targetPackage == null) {
outError[0] = "<instrumentation> does not specify targetPackage";
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
if (!parseAllMetaData(res, parser, attrs, "<instrumentation>", a,
outError)) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
owner.instrumentation.add(a);
return a;
}
private boolean parseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
String name = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_name, 0);
if (name != null) {
ai.className = buildClassName(pkgName, name, outError);
if (ai.className == null) {
sa.recycle();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
String manageSpaceActivity = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity, 0);
if (manageSpaceActivity != null) {
ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
outError);
}
boolean allowBackup = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
// backupAgent, killAfterRestore, and restoreAnyVersion are only relevant
// if backup is possible for the given application.
String backupAgent = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent, 0);
if (backupAgent != null) {
ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
if (DEBUG_BACKUP) {
Slog.v(TAG, "android:backupAgent = " + ai.backupAgentName
+ " from " + pkgName + "+" + backupAgent);
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
true)) {
ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_restoreAnyVersion,
false)) {
ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
}
}
}
TypedValue v = sa.peekValue(
com.android.internal.R.styleable.AndroidManifestApplication_label);
if (v != null && (ai.labelRes=v.resourceId) == 0) {
ai.nonLocalizedLabel = v.coerceToString();
}
ai.icon = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_icon, 0);
ai.logo = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_logo, 0);
ai.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
ai.descriptionRes = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
if ((flags&PARSE_IS_SYSTEM) != 0) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_persistent,
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_debuggable,
false)) {
ai.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_vmSafeMode,
false)) {
ai.flags |= ApplicationInfo.FLAG_VM_SAFE_MODE;
}
boolean hardwareAccelerated = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
true)) {
ai.flags |= ApplicationInfo.FLAG_HAS_CODE;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowTaskReparenting,
false)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_allowClearUserData,
true)) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_testOnly,
false)) {
ai.flags |= ApplicationInfo.FLAG_TEST_ONLY;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_largeHeap,
false)) {
ai.flags |= ApplicationInfo.FLAG_LARGE_HEAP;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_supportsRtl,
false /* default is no RTL support*/)) {
ai.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL;
}
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
ai.permission = (str != null && str.length() > 0) ? str.intern() : null;
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity, 0);
} 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.
str = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity);
}
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
str, outError);
if (outError[0] == null) {
CharSequence pname;
if (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_process, 0);
} 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(
com.android.internal.R.styleable.AndroidManifestApplication_process);
}
ai.processName = buildProcessName(ai.packageName, null, pname,
flags, mSeparateProcesses, outError);
ai.enabled = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_enabled, true);
if (false) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
false)) {
ai.flags |= ApplicationInfo.FLAG_CANT_SAVE_STATE;
// A heavy-weight application can not be in a custom process.
// We can do direct compare because we intern all strings.
if (ai.processName != null && ai.processName != ai.packageName) {
outError[0] = "cantSaveState applications can not use custom processes";
}
}
}
}
ai.uiOptions = sa.getInt(
com.android.internal.R.styleable.AndroidManifestApplication_uiOptions, 0);
sa.recycle();
if (outError[0] != null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
final int innerDepth = parser.getDepth();
int type;
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("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
hardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("uses-library")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
sa.recycle();
if (lname != null) {
if (req) {
if (owner.usesLibraries == null) {
owner.usesLibraries = new ArrayList<String>();
}
if (!owner.usesLibraries.contains(lname)) {
owner.usesLibraries.add(lname.intern());
}
} else {
if (owner.usesOptionalLibraries == null) {
owner.usesOptionalLibraries = new ArrayList<String>();
}
if (!owner.usesOptionalLibraries.contains(lname)) {
owner.usesOptionalLibraries.add(lname.intern());
}
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
// Dependencies for app installers; we don't currently try to
// enforce this.
XmlUtils.skipCurrentTag(parser);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <application>: " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
return true;
}
private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
String[] outError, String tag, TypedArray sa,
int nameRes, int labelRes, int iconRes, int logoRes) {
String name = sa.getNonConfigurationString(nameRes, 0);
if (name == null) {
outError[0] = tag + " does not specify android:name";
return false;
}
outInfo.name
= buildClassName(owner.applicationInfo.packageName, name, outError);
if (outInfo.name == null) {
return false;
}
int iconVal = sa.getResourceId(iconRes, 0);
if (iconVal != 0) {
outInfo.icon = iconVal;
outInfo.nonLocalizedLabel = null;
}
int logoVal = sa.getResourceId(logoRes, 0);
if (logoVal != 0) {
outInfo.logo = logoVal;
}
TypedValue v = sa.peekValue(labelRes);
if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
outInfo.nonLocalizedLabel = v.coerceToString();
}
outInfo.packageName = owner.packageName;
return true;
}
private Activity parseActivity(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestActivity);
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
com.android.internal.R.styleable.AndroidManifestActivity_name,
com.android.internal.R.styleable.AndroidManifestActivity_label,
com.android.internal.R.styleable.AndroidManifestActivity_icon,
com.android.internal.R.styleable.AndroidManifestActivity_logo,
mSeparateProcesses,
com.android.internal.R.styleable.AndroidManifestActivity_process,
com.android.internal.R.styleable.AndroidManifestActivity_description,
com.android.internal.R.styleable.AndroidManifestActivity_enabled);
}
mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
return null;
}
boolean setExported = sa.hasValue(
com.android.internal.R.styleable.AndroidManifestActivity_exported);
if (setExported) {
a.info.exported = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_exported, false);
}
a.info.theme = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifestActivity_theme, 0);
a.info.uiOptions = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_uiOptions,
a.info.applicationInfo.uiOptions);
String parentName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestActivity_parentActivityName, 0);
if (parentName != null) {
String parentClassName = buildClassName(a.info.packageName, parentName, outError);
if (outError[0] == null) {
a.info.parentActivityName = parentClassName;
} else {
Log.e(TAG, "Activity " + a.info.name + " specified invalid parentActivityName " +
parentName);
outError[0] = null;
}
}
String str;
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestActivity_permission, 0);
if (str == null) {
a.info.permission = owner.applicationInfo.permission;
} else {
a.info.permission = str.length() > 0 ? str.toString().intern() : null;
}
str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity, 0);
a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName,
owner.applicationInfo.taskAffinity, str, outError);
a.info.flags = 0;
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_multiprocess,
false)) {
a.info.flags |= ActivityInfo.FLAG_MULTIPROCESS;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_finishOnTaskLaunch,
false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_clearTaskOnLaunch,
false)) {
a.info.flags |= ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_noHistory,
false)) {
a.info.flags |= ActivityInfo.FLAG_NO_HISTORY;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_alwaysRetainTaskState,
false)) {
a.info.flags |= ActivityInfo.FLAG_ALWAYS_RETAIN_TASK_STATE;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_stateNotNeeded,
false)) {
a.info.flags |= ActivityInfo.FLAG_STATE_NOT_NEEDED;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_excludeFromRecents,
false)) {
a.info.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_allowTaskReparenting,
(owner.applicationInfo.flags&ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING) != 0)) {
a.info.flags |= ActivityInfo.FLAG_ALLOW_TASK_REPARENTING;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_finishOnCloseSystemDialogs,
false)) {
a.info.flags |= ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_showOnLockScreen,
false)) {
a.info.flags |= ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_immersive,
false)) {
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
if (!receiver) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
hardwareAccelerated)) {
a.info.flags |= ActivityInfo.FLAG_HARDWARE_ACCELERATED;
}
a.info.launchMode = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
ActivityInfo.LAUNCH_MULTIPLE);
a.info.screenOrientation = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
a.info.configChanges = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_configChanges,
0);
a.info.softInputMode = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
0);
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
}
if (receiver) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_singleUser,
false)) {
a.info.flags |= ActivityInfo.FLAG_SINGLE_USER;
if (a.info.exported) {
Slog.w(TAG, "Activity exported request ignored due to singleUser: "
+ a.className + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
a.info.exported = false;
}
setExported = true;
}
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly,
false)) {
a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
}
}
sa.recycle();
if (receiver && (owner.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
// A heavy-weight application can not have receives in its main process
// We can do direct compare because we intern all strings.
if (a.info.processName == owner.packageName) {
outError[0] = "Heavy-weight applications can not have receivers in main process";
}
}
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")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, attrs, flags, intent, outError, !receiver)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
a.intents.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
outError)) == null) {
return null;
}
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ 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) {
a.info.exported = a.intents.size() > 0;
}
return a;
}
private Activity parseActivityAlias(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestActivityAlias);
String targetActivity = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity, 0);
if (targetActivity == null) {
outError[0] = "<activity-alias> does not specify android:targetActivity";
sa.recycle();
return null;
}
targetActivity = buildClassName(owner.applicationInfo.packageName,
targetActivity, outError);
if (targetActivity == null) {
sa.recycle();
return null;
}
if (mParseActivityAliasArgs == null) {
mParseActivityAliasArgs </