Skip to content

Commit be10e4c

Browse files
committed
A few steps more
1 parent 45685c7 commit be10e4c

File tree

8 files changed

+199
-7
lines changed

8 files changed

+199
-7
lines changed

tools/apput/src/AssemblyStore/AssemblyStore.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
using System.Collections.Generic;
33
using System.IO;
44

5+
using Xamarin.Android.Tools;
6+
57
namespace ApplicationUtility;
68

79
public class AssemblyStore : IAspect
810
{
911
public static string AspectName { get; } = "Assembly Store";
1012

1113
public IDictionary<string, ApplicationAssembly> Assemblies { get; private set; } = null!;
12-
public NativeArchitecture Architecture { get; private set; } = NativeArchitecture.Unknown;
14+
public AndroidTargetArch Architecture { get; private set; } = AndroidTargetArch.None;
1315
public ulong NumberOfAssemblies => (ulong)(Assemblies?.Count ?? 0);
1416

1517
public static IAspect LoadAspect (Stream stream, string? description)

tools/apput/src/Common/Log.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ public static void Debug (string message = "")
123123
Debug (tag: String.Empty, message);
124124
}
125125

126+
// TODO: debug should go to file if verbose output isn't enabled
126127
public static void Debug (string tag, string message)
127128
{
128129
if (!showDebug) {
@@ -140,6 +141,16 @@ public static void Debug (string tag, string message)
140141
WriteLine (message);
141142
}
142143

144+
public static void Debug (string message, Exception ex)
145+
{
146+
if (!showDebug) {
147+
return;
148+
}
149+
150+
Debug (tag: String.Empty, message);
151+
Debug (tag: String.Empty, ex.ToString ());
152+
}
153+
143154
public static void ExceptionError (string message, Exception ex)
144155
{
145156
Log.Error (message);

tools/apput/src/Package/ApplicationPackage.cs

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
using System.IO.Compression;
55
using System.Linq;
66

7+
using Xamarin.Android.Tasks;
8+
using Xamarin.Android.Tools;
9+
710
namespace ApplicationUtility;
811

912
public abstract class ApplicationPackage : IAspect
@@ -24,18 +27,28 @@ public abstract class ApplicationPackage : IAspect
2427
"dex/classes.dex",
2528
};
2629

30+
readonly static HashSet<string> KnownSignatureEntries = new (StringComparer.Ordinal) {
31+
"META-INF/BNDLTOOL.RSA",
32+
"META-INF/ANDROIDD.RSA",
33+
};
34+
2735
public static string AspectName { get; } = "Application package";
2836

2937
public abstract string PackageFormat { get; }
38+
protected abstract string NativeLibDirBase { get; }
39+
protected abstract string AndroidManifestPath { get; }
3040

3141
protected ZipArchive Zip { get; }
3242
public string? Description { get; }
3343

3444
public bool Signed { get; protected set; }
45+
public bool ValidAndroidPackage { get; protected set; }
46+
public bool Debuggable { get; protected set; }
3547
public ApplicationRuntime Runtime { get; protected set; } = ApplicationRuntime.Unknown;
3648
public string PackageName { get; protected set; } = "";
49+
public string MainActivity { get; protected set; } = "";
3750
public List<AssemblyStore>? AssemblyStores { get; protected set; }
38-
public NativeArchitecture Architectures { get; protected set; }
51+
public List<AndroidTargetArch> Architectures { get; protected set; } = new ();
3952

4053
protected ApplicationPackage (ZipArchive zip, string? description)
4154
{
@@ -61,11 +74,147 @@ public static IAspect LoadAspect (Stream stream, string? description)
6174
} else {
6275
throw new InvalidOperationException ("Stream is not a supported Android ZIP package. Call ProbeAspect first.");
6376
}
64-
6577
Log.Debug ($"ApplicationPackage: stream ('{description}') is: {ret.PackageFormat}");
78+
79+
// TODO: for all of the below, add support for detection of older XA apps (just to warn that this version doesn't support
80+
// and that people should use older tools)
81+
ret.TryDetectArchitectures (); // This must be called first, some further steps depend on it
82+
ret.TryDetectRuntime ();
83+
ret.TryDetectWhetherIsSigned ();
84+
ret.TryLoadAssemblyStores ();
85+
ret.TryLoadAndroidManifest ();
86+
6687
return ret;
6788
}
6889

90+
void TryDetectArchitectures ()
91+
{
92+
foreach (AndroidTargetArch arch in Enum.GetValues <AndroidTargetArch> ()) {
93+
if (!MonoAndroidHelper.SupportedTargetArchitectures.Contains (arch)) {
94+
continue;
95+
}
96+
97+
// We can't simply test for presence of the libDir below, because it's possible
98+
// that a separate entry for the "directory" (they are only a naming convention
99+
// in the ZIP archive, not a separate entity) won't exist. Instead, we look for
100+
// any entry starting with the path.
101+
if (!HasEntryStartingWith (Zip, GetNativeLibDir (arch))) {
102+
continue;
103+
}
104+
Architectures.Add (arch);
105+
Log.Debug ($"Detected architecture: {arch}");
106+
}
107+
}
108+
109+
void TryDetectRuntime ()
110+
{
111+
ApplicationRuntime runtime = ApplicationRuntime.Unknown;
112+
string runtimePath;
113+
foreach (AndroidTargetArch arch in Architectures) {
114+
runtimePath = GetNativeLibFile (arch, "libcoreclr.so");
115+
if (HasEntry (Zip, runtimePath)) {
116+
runtime = ApplicationRuntime.CoreCLR;
117+
break;
118+
}
119+
120+
runtimePath = GetNativeLibFile (arch, "libmonosgen-2.0.so");
121+
if (HasEntry (Zip, runtimePath)) {
122+
runtime = ApplicationRuntime.MonoVM;
123+
break;
124+
}
125+
}
126+
127+
if (runtime != ApplicationRuntime.Unknown || Architectures.Count == 0) {
128+
Log.Debug ($"Detected runtime: {runtime}");
129+
return;
130+
}
131+
132+
runtimePath = GetNativeLibFile (Architectures[0], "libmonodroid.so");
133+
if (!HasEntry (Zip, runtimePath)) {
134+
return;
135+
}
136+
137+
// TODO: it might be statically linked CoreCLR runtime. Need to check for presence of
138+
// some public symbols to verify that.
139+
}
140+
141+
void TryDetectWhetherIsSigned ()
142+
{
143+
Signed = HasAnyEntries (Zip, KnownSignatureEntries);
144+
Log.Debug ($"Signature detected: {Signed}");
145+
}
146+
147+
void TryLoadAssemblyStores ()
148+
{
149+
foreach (AndroidTargetArch arch in Architectures) {
150+
string storePath = GetNativeLibFile (arch, $"libassemblies.{MonoAndroidHelper.ArchToAbi (arch)}.blob.so");
151+
Log.Debug ($"Trying assembly store: {storePath}");
152+
if (!HasEntry (Zip, storePath)) {
153+
Log.Debug ($"Assembly store '{storePath}' not found");
154+
continue;
155+
}
156+
157+
Log.Debug ($"Found assembly store entry for architecture {arch}");
158+
AssemblyStore? store = TryLoadAssemblyStore (storePath);
159+
if (store == null) {
160+
continue;
161+
}
162+
}
163+
}
164+
165+
AssemblyStore? TryLoadAssemblyStore (string storePath)
166+
{
167+
// AssemblyStore class owns the stream, don't dispose it here
168+
Stream? storeStream = TryGetEntryStream (storePath);
169+
if (storeStream == null) {
170+
return null;
171+
}
172+
173+
try {
174+
if (!AssemblyStore.ProbeAspect (storeStream, storePath)) {
175+
Log.Debug ($"Assembly store '{storePath}' is not in a supported format");
176+
return null;
177+
}
178+
179+
return (AssemblyStore)AssemblyStore.LoadAspect (storeStream, storePath);
180+
} catch (Exception ex) {
181+
Log.Debug ($"Failed to load assembly store '{storePath}'", ex);
182+
return null;
183+
}
184+
}
185+
186+
void TryLoadAndroidManifest ()
187+
{
188+
ValidAndroidPackage = HasEntry (Zip, AndroidManifestPath);
189+
if (!ValidAndroidPackage) {
190+
Log.Debug ($"Package is missing manifest entry '{AndroidManifestPath}'");
191+
return;
192+
}
193+
194+
Log.Debug ($"Found Android manifest '{AndroidManifestPath}'");
195+
using Stream? manifestStream = TryGetEntryStream (AndroidManifestPath);
196+
// TODO: parse
197+
}
198+
199+
string GetNativeLibDir (AndroidTargetArch arch) => $"{NativeLibDirBase}/{MonoAndroidHelper.ArchToAbi (arch)}/";
200+
string GetNativeLibFile (AndroidTargetArch arch, string fileName) => $"{GetNativeLibDir (arch)}{fileName}";
201+
202+
Stream? TryGetEntryStream (string path)
203+
{
204+
try {
205+
ZipArchiveEntry? entry = Zip.GetEntry (path);
206+
if (entry == null) {
207+
Log.Debug ($"ZIP entry '{path}' could not be loaded.");
208+
return null;
209+
}
210+
211+
return entry.Open ();
212+
} catch (Exception ex) {
213+
Log.Debug ($"Failed to load entry '{path}' from the archive.", ex);
214+
return null;
215+
}
216+
}
217+
69218
public static bool ProbeAspect (Stream stream, string? description)
70219
{
71220
Log.Debug ($"ApplicationPackage: checking if stream ('{description}') is a ZIP archive");
@@ -91,15 +240,30 @@ public static bool ProbeAspect (Stream stream, string? description)
91240
return true;
92241
}
93242

94-
static bool IsAPK (ZipArchive zip) => HasEntries (zip, KnownApkEntries);
95-
static bool IsAAB (ZipArchive zip) => HasEntries (zip, KnownAabEntries);
96-
static bool IsBase (ZipArchive zip) => HasEntries (zip, KnownBaseEntries);
243+
static bool IsAPK (ZipArchive zip) => HasAllEntries (zip, KnownApkEntries);
244+
static bool IsAAB (ZipArchive zip) => HasAllEntries (zip, KnownAabEntries);
245+
static bool IsBase (ZipArchive zip) => HasAllEntries (zip, KnownBaseEntries);
97246

98-
static bool HasEntries (ZipArchive zip, HashSet<string> knownEntries)
247+
static bool HasAnyEntries (ZipArchive zip, HashSet<string> knownEntries)
99248
{
100249
return zip.Entries.Where ((ZipArchiveEntry entry) => knownEntries.Contains (entry.FullName)).Any ();
101250
}
102251

252+
static bool HasAllEntries (ZipArchive zip, HashSet<string> knownEntries)
253+
{
254+
return zip.Entries.Where ((ZipArchiveEntry entry) => knownEntries.Contains (entry.FullName)).Count () == knownEntries.Count;
255+
}
256+
257+
static bool HasEntry (ZipArchive zip, string path)
258+
{
259+
return zip.Entries.Where ((ZipArchiveEntry entry) => entry.FullName == path).Any ();
260+
}
261+
262+
static bool HasEntryStartingWith (ZipArchive zip, string path)
263+
{
264+
return zip.Entries.Where ((ZipArchiveEntry entry) => entry.FullName.StartsWith (path, StringComparison.Ordinal)).Any ();
265+
}
266+
103267
static ZipArchive? TryOpenAsZip (Stream stream)
104268
{
105269
stream.Seek (0, SeekOrigin.Begin);

tools/apput/src/Package/ApplicationRuntime.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ public enum ApplicationRuntime
55
Unknown,
66
MonoVM,
77
CoreCLR,
8+
StaticCoreCLR,
89
}

tools/apput/src/Package/PackageAAB.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace ApplicationUtility;
55
class PackageAAB : ApplicationPackage
66
{
77
public override string PackageFormat { get; } = "AAB package";
8+
protected override string NativeLibDirBase => "base/lib";
9+
protected override string AndroidManifestPath => "base/manifest/AndroidManifest.xml";
810

911
public PackageAAB (ZipArchive zip, string? description)
1012
: base (zip, description)

tools/apput/src/Package/PackageAPK.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace ApplicationUtility;
55
class PackageAPK : ApplicationPackage
66
{
77
public override string PackageFormat { get; } = "APK package";
8+
protected override string NativeLibDirBase => "lib";
9+
protected override string AndroidManifestPath => "AndroidManifest.xml";
810

911
public PackageAPK (ZipArchive zip, string? description)
1012
: base (zip, description)

tools/apput/src/Package/PackageBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace ApplicationUtility;
55
class PackageBase : ApplicationPackage
66
{
77
public override string PackageFormat { get; } = "Base application package";
8+
protected override string NativeLibDirBase => "lib";
9+
protected override string AndroidManifestPath => "manifest/AndroidManifest.xml";
810

911
public PackageBase (ZipArchive zip, string? description)
1012
: base (zip, description)

tools/apput/src/apput.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@
2424
</ItemGroup>
2525

2626
<Import Project="$(XAPackagesDir)\Xamarin.LibZipSharp.$(LibZipSharpVersion)\build\Xamarin.LibZipSharp.targets" Condition="Exists('$(XAPackagesDir)\Xamarin.LibZipSharp.$(LibZipSharpVersion)\build\Xamarin.LibZipSharp.targets')" />
27+
28+
<ItemGroup>
29+
<ProjectReference Include="..\..\..\external\xamarin-android-tools\src\Xamarin.Android.Tools.AndroidSdk\Xamarin.Android.Tools.AndroidSdk.csproj" />
30+
</ItemGroup>
31+
32+
<ItemGroup>
33+
<Compile Include="..\..\..\src\Xamarin.Android.Build.Tasks\Utilities\MonoAndroidHelper.Basic.cs" />
34+
</ItemGroup>
2735
</Project>

0 commit comments

Comments
 (0)