public static Chunk Get(BinaryReader input) { if ((input.BaseStream.Remaining()) < HeaderMinSizeBytes) { // Android ignores the last chunk if its header is too big to fit into the file input.BaseStream.Position = input.BaseStream.Length; return(null); } long originalPosition = input.BaseStream.Position; int type = input.ReadUInt16(); int headerSize = input.ReadUInt16(); long chunkSize = input.ReadUInt32(); long chunkRemaining = chunkSize - 8; if (chunkRemaining > input.BaseStream.Remaining()) { // Android ignores the last chunk if it's too big to fit into the file input.BaseStream.Position = input.BaseStream.Length; return(null); } if (headerSize < HeaderMinSizeBytes) { throw new FormatException( "Malformed chunk: header too short: " + headerSize + " bytes"); } else if (headerSize > chunkSize) { throw new FormatException( "Malformed chunk: header too long: " + headerSize + " bytes. Chunk size: " + chunkSize + " bytes"); } long contentStartPosition = originalPosition + headerSize; long chunkEndPosition = originalPosition + chunkSize; Chunk chunk = new Chunk( type, AndroidBinXmlParser.SliceFromTo(input.BaseStream, originalPosition, contentStartPosition), AndroidBinXmlParser.SliceFromTo(input.BaseStream, contentStartPosition, chunkEndPosition)); input.BaseStream.Position = chunkEndPosition; return(chunk); }
/// <summary> /// Returns the lowest Android platform version (API Level) supported by an APK with the /// provided <code>AndroidManifest.xml</code> /// </summary> /// <param name="androidManifest">contents of <code>AndroidManifest.xml</code> in binary Android resource format</param> /// <returns></returns> public static int GetMinSdkVersionFromBinaryAndroidManifest(byte[] androidManifest) { // IMPLEMENTATION NOTE: Minimum supported Android platform version number is declared using // uses-sdk elements which are children of the top-level manifest element. uses-sdk element // declares the minimum supported platform version using the android:minSdkVersion attribute // whose default value is 1. // For each encountered uses-sdk element, the Android runtime checks that its minSdkVersion // is not higher than the runtime's API Level and rejects APKs if it is higher. Thus, the // effective minSdkVersion value is the maximum over the encountered minSdkVersion values. try { // If no uses-sdk elements are encountered, Android accepts the APK. We treat this // scenario as though the minimum supported API Level is 1. int result = 1; AndroidBinXmlParser parser = new AndroidBinXmlParser(androidManifest); var eventType = parser.EventType; while (eventType != AndroidBinXmlParser.EndOfDocument) { if ((eventType == AndroidBinXmlParser.EventStartElement) && (parser.Depth == 2) && ("uses-sdk" == parser.Name) && (String.IsNullOrEmpty(parser.Namespace))) { // In each uses-sdk element, minSdkVersion defaults to 1 int minSdkVersion = 1; for (int i = 0; i < parser.AttributeCount; i++) { if (parser.GetAttributeNameResourceId(i) == MinSdkVersionAttrId) { int valueType = parser.GetAttributeValueType(i); switch (valueType) { case AndroidBinXmlParser.ValueTypeInt: minSdkVersion = parser.GetAttributeIntValue(i); break; case AndroidBinXmlParser.ValueTypeString: minSdkVersion = GetMinSdkVersionForCodename(parser.GetAttributeStringValue(i)); break; default: throw new FormatException( "Unable to determine APK's minimum supported Android" + ": unsupported value type in " + AndroidManifestZipEntryName + "'s" + " minSdkVersion" + ". Only integer values supported."); } break; } } result = Math.Max(result, minSdkVersion); } eventType = parser.Next(); } return(result); } catch (Exception e) { throw new FormatException( "Unable to determine APK's minimum supported Android platform version" + ": malformed binary resource: " + AndroidManifestZipEntryName, e); } }