Skip to content

Commit 036b244

Browse files
committed
New approach. All the utilities will be in a single assembly.
1 parent 747041d commit 036b244

File tree

11 files changed

+321
-0
lines changed

11 files changed

+321
-0
lines changed

Xamarin.Android.sln

+7
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proguard-android", "src\pro
129129
EndProject
130130
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Runtime.NativeAOT", "src\Microsoft.Android.Runtime.NativeAOT\Microsoft.Android.Runtime.NativeAOT.csproj", "{E8831F32-11D7-D42C-E43C-711998BC357A}"
131131
EndProject
132+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.AppTools", "tools\Microsoft.Android.AppTools\Microsoft.Android.AppTools.csproj", "{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}"
133+
EndProject
132134
Global
133135
GlobalSection(SolutionConfigurationPlatforms) = preSolution
134136
Debug|AnyCPU = Debug|AnyCPU
@@ -359,6 +361,10 @@ Global
359361
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
360362
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
361363
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.Build.0 = Release|Any CPU
364+
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
365+
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Debug|AnyCPU.Build.0 = Debug|Any CPU
366+
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.ActiveCfg = Release|Any CPU
367+
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8}.Release|AnyCPU.Build.0 = Release|Any CPU
362368
EndGlobalSection
363369
GlobalSection(SolutionProperties) = preSolution
364370
HideSolutionNode = FALSE
@@ -420,6 +426,7 @@ Global
420426
{5E806C9F-1B67-4B6B-A6AB-258834250DBB} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
421427
{5FD0133B-69E5-4474-9B67-9FD1D0150C70} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
422428
{E8831F32-11D7-D42C-E43C-711998BC357A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
429+
{CDDB9868-704F-461A-9A9C-BAA0DFBC56D8} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
423430
EndGlobalSection
424431
GlobalSection(ExtensibilityGlobals) = postSolution
425432
SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
using Xamarin.Android.Tools;
4+
5+
namespace Microsoft.Android.AppTools.Assemblies;
6+
7+
public class AssemblyStore
8+
{
9+
public uint FullFormatVersion { get; private set; } = 0;
10+
public ulong NumberOfAssemblies { get; private set; } = 0;
11+
public AndroidTargetArch TargetArchitecture { get; private set; } = AndroidTargetArch.None;
12+
public Version? Version { get; private set; }
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Xamarin.Android.Tools;
2+
3+
namespace Microsoft.Android.AppTools;
4+
5+
public class SharedLibrary
6+
{
7+
public SharedLibraryKind Kind { get; private set; } = SharedLibraryKind.Other;
8+
public AndroidTargetArch TargetArchitecture { get; private set; } = AndroidTargetArch.None;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace Microsoft.Android.AppTools;
2+
3+
public enum SharedLibraryKind
4+
{
5+
XamarinApp,
6+
MonoRuntime,
7+
CoreClrRuntime,
8+
BCL,
9+
Other,
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>$(DotNetStableTargetFramework)</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<PackageReference Include="Xamarin.LibZipSharp" Version="$(LibZipSharpVersion)" />
10+
<PackageReference Include="K4os.Compression.LZ4" Version="$(LZ4PackageVersion)" />
11+
<PackageReference Include="System.IO.Hashing" Version="$(SystemIOHashingPackageVersion)" />
12+
<PackageReference Include="ELFSharp" Version="$(ELFSharpVersion)" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\..\external\xamarin-android-tools\src\Xamarin.Android.Tools.AndroidSdk\Xamarin.Android.Tools.AndroidSdk.csproj" />
17+
</ItemGroup>
18+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
4+
using Microsoft.Android.AppTools.Assemblies;
5+
using Xamarin.Android.Tools;
6+
7+
namespace Microsoft.Android.AppTools;
8+
9+
/// <summary>
10+
/// Main application information class. Gathers information about the application, or components thereof,
11+
/// regardless of whether the data is gathered from an application archive or just a single file (e.g.
12+
/// the assembly store) that makes part of the application.
13+
/// </summary>
14+
public class ApplicationInfo
15+
{
16+
readonly ILogger log;
17+
18+
/// <summary>
19+
/// If information was obtained from an application archive (`.apk`, `.aab` or `.zip`), this
20+
/// property will be set accordingly to a value different than <see cref="ArchiveKind.None" />
21+
/// </summary>
22+
public ArchiveKind ArchiveKind { get; private set; } = ArchiveKind.None;
23+
24+
/// <summary>
25+
/// If assembly store was read, either from the archive or directly from a file on the filesystem,
26+
/// this property will contain instance of the <see cref="AssemblyStore" /> class describing the
27+
/// store in detail.
28+
/// </summary>
29+
public AssemblyStore? AssemblyStore { get; private set; }
30+
31+
/// <summary>
32+
/// If application info was obtained from an application archive (`.apk`, `.aab` or `.zip`) or
33+
/// from `AndroidManifest.xml`, this property will contain the application's package name (if
34+
/// found in the manifest).
35+
/// </summary>
36+
public string? PackageName { get; private set; }
37+
38+
/// <summary>
39+
/// If application info was obtained from an application archive (`.apk`, `.aab` or `.zip`) or
40+
/// by directly reading a `.so` shared library, this collection will contain information about
41+
/// all the shared libraries for all the target architectures supported by the application.
42+
/// </summary>
43+
public ICollection<SharedLibrary>? SharedLibraries { get; private set; }
44+
45+
/// <summary>
46+
/// If application info was obtained from an application archive (`.apk`, `.aab` or `.zip`) or
47+
/// by directly reading a `.so` shared library, this collection will contain all the architectures
48+
/// targeted by the application.
49+
/// </summary>
50+
public ICollection<AndroidTargetArch>? TargetArchitectures { get; private set; }
51+
52+
public ApplicationInfo (ILogger log)
53+
{
54+
this.log = log;
55+
}
56+
57+
/// <summary>
58+
/// Reads application information from the file passed in the `inputFilePath` parameter. The file
59+
/// doesn't have to be application .apk or .aab archive, it can be any file that is (or is not)
60+
/// part of the application. If the file is unrecognized, unsupported etc, the constructor will
61+
/// make a note of it and initialize the class accordingly. All the issues will be reported via
62+
/// calls to members of the `ILogger` interface, passed in the `log` parameter to the class
63+
/// constructor. Returns `true` if there was any valid information read, `false` otherwise.
64+
/// </summary>
65+
public bool Read (string inputFilePath)
66+
{
67+
(FileFormat format, FileInfo? info) = Utils.DetectFileFormat (log, inputFilePath);
68+
if (info == null || format == FileFormat.Unknown) {
69+
return false;
70+
}
71+
72+
return false;
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Microsoft.Android.AppTools;
2+
3+
public enum ArchiveKind
4+
{
5+
None,
6+
APK,
7+
AAB,
8+
ZIP,
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace Microsoft.Android.AppTools;
2+
3+
enum ELFPayloadError
4+
{
5+
None,
6+
NotELF,
7+
LoadFailed,
8+
NotSharedLibrary,
9+
NotLittleEndian,
10+
NoPayloadSection,
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Microsoft.Android.AppTools;
2+
3+
enum FileFormat
4+
{
5+
Aab,
6+
AabBase,
7+
Apk,
8+
AssemblyStore,
9+
ELF,
10+
Zip,
11+
Unknown,
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Microsoft.Android.AppTools;
2+
3+
public interface ILogger
4+
{
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using System;
2+
using System.IO;
3+
using System.Buffers;
4+
5+
using ELFSharp.ELF;
6+
using ELFSharp.ELF.Sections;
7+
using Xamarin.Tools.Zip;
8+
9+
namespace Microsoft.Android.AppTools;
10+
11+
static class Utils
12+
{
13+
static readonly string[] aabZipEntries = {
14+
"base/manifest/AndroidManifest.xml",
15+
"BundleConfig.pb",
16+
};
17+
18+
static readonly string[] aabBaseZipEntries = {
19+
"manifest/AndroidManifest.xml",
20+
};
21+
22+
static readonly string[] apkZipEntries = {
23+
"AndroidManifest.xml",
24+
};
25+
26+
public const uint ZIP_MAGIC = 0x4034b50;
27+
public const uint ASSEMBLY_STORE_MAGIC = 0x41424158;
28+
public const uint ELF_MAGIC = 0x464c457f;
29+
30+
public static readonly ArrayPool<byte> BytePool = ArrayPool<byte>.Shared;
31+
32+
public static (ulong offset, ulong size, ELFPayloadError error) FindELFPayloadSectionOffsetAndSize (Stream stream)
33+
{
34+
stream.Seek (0, SeekOrigin.Begin);
35+
Class elfClass = ELFReader.CheckELFType (stream);
36+
if (elfClass == Class.NotELF) {
37+
return ReturnError (null, ELFPayloadError.NotELF);
38+
}
39+
40+
if (!ELFReader.TryLoad (stream, shouldOwnStream: false, out IELF? elf)) {
41+
return ReturnError (elf, ELFPayloadError.LoadFailed);
42+
}
43+
44+
if (elf.Type != FileType.SharedObject) {
45+
return ReturnError (elf, ELFPayloadError.NotSharedLibrary);
46+
}
47+
48+
if (elf.Endianess != ELFSharp.Endianess.LittleEndian) {
49+
return ReturnError (elf, ELFPayloadError.NotLittleEndian);
50+
}
51+
52+
if (!elf.TryGetSection ("payload", out ISection? payloadSection)) {
53+
return ReturnError (elf, ELFPayloadError.NoPayloadSection);
54+
}
55+
56+
bool is64 = elf.Machine switch {
57+
Machine.ARM => false,
58+
Machine.Intel386 => false,
59+
60+
Machine.AArch64 => true,
61+
Machine.AMD64 => true,
62+
63+
_ => throw new NotSupportedException ($"Unsupported ELF architecture '{elf.Machine}'")
64+
};
65+
66+
ulong offset;
67+
ulong size;
68+
69+
if (is64) {
70+
(offset, size) = GetOffsetAndSize64 ((Section<ulong>)payloadSection);
71+
} else {
72+
(offset, size) = GetOffsetAndSize32 ((Section<uint>)payloadSection);
73+
}
74+
75+
elf.Dispose ();
76+
return (offset, size, ELFPayloadError.None);
77+
78+
(ulong offset, ulong size) GetOffsetAndSize64 (Section<ulong> payload)
79+
{
80+
return (payload.Offset, payload.Size);
81+
}
82+
83+
(ulong offset, ulong size) GetOffsetAndSize32 (Section<uint> payload)
84+
{
85+
return ((ulong)payload.Offset, (ulong)payload.Size);
86+
}
87+
88+
(ulong offset, ulong size, ELFPayloadError error) ReturnError (IELF? elf, ELFPayloadError error)
89+
{
90+
elf?.Dispose ();
91+
92+
return (0, 0, error);
93+
}
94+
}
95+
96+
public static (FileFormat format, FileInfo? info) DetectFileFormat (ILogger log, string path)
97+
{
98+
if (String.IsNullOrEmpty (path)) {
99+
return (FileFormat.Unknown, null);
100+
}
101+
102+
var info = new FileInfo (path);
103+
if (!info.Exists) {
104+
return (FileFormat.Unknown, null);
105+
}
106+
107+
using var reader = new BinaryReader (info.OpenRead ());
108+
109+
// ATM, all formats we recognize have 4-byte magic at the start
110+
FileFormat format = reader.ReadUInt32 () switch {
111+
Utils.ZIP_MAGIC => FileFormat.Zip,
112+
Utils.ELF_MAGIC => FileFormat.ELF,
113+
Utils.ASSEMBLY_STORE_MAGIC => FileFormat.AssemblyStore,
114+
_ => FileFormat.Unknown
115+
};
116+
117+
if (format == FileFormat.Unknown || format != FileFormat.Zip) {
118+
return (format, info);
119+
}
120+
121+
return (DetectAndroidArchive (info, format), info);
122+
}
123+
124+
static FileFormat DetectAndroidArchive (FileInfo info, FileFormat defaultFormat)
125+
{
126+
using var zip = ZipArchive.Open (info.FullName, FileMode.Open);
127+
128+
if (HasAllEntries (zip, aabZipEntries)) {
129+
return FileFormat.Aab;
130+
}
131+
132+
if (HasAllEntries (zip, apkZipEntries)) {
133+
return FileFormat.Apk;
134+
}
135+
136+
if (HasAllEntries (zip, aabBaseZipEntries)) {
137+
return FileFormat.AabBase;
138+
}
139+
140+
return defaultFormat;
141+
}
142+
143+
static bool HasAllEntries (ZipArchive zip, string[] entries)
144+
{
145+
foreach (string entry in entries) {
146+
if (!zip.ContainsEntry (entry, caseSensitive: true)) {
147+
return false;
148+
}
149+
}
150+
151+
return true;
152+
}
153+
}

0 commit comments

Comments
 (0)