Skip to content

Commit 2d2b75f

Browse files
committed
Application package reporter continued
1 parent 1bf1a91 commit 2d2b75f

File tree

4 files changed

+188
-7
lines changed

4 files changed

+188
-7
lines changed

tools/apput/src/Android/AndroidManifest.cs

Lines changed: 132 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ namespace ApplicationUtility;
77
public class AndroidManifest : IAspect
88
{
99
public string Description { get; }
10+
public string? PackageName { get; }
1011

11-
AXMLParser? binaryParser;
1212
XmlDocument? xmlDoc;
13+
XmlNamespaceManager? nsmgr;
1314

1415
AndroidManifest (AXMLParser binaryParser, string? description)
1516
{
1617
Description = String.IsNullOrEmpty (description) ? "Android manifest" : description;
17-
this.binaryParser = binaryParser;
18+
Read (binaryParser);
19+
20+
nsmgr = PrepareForReading (xmlDoc);
21+
PackageName = TryGetPackageName (xmlDoc, nsmgr);
1822
}
1923

2024
public static IAspect LoadAspect (Stream stream, IAspectState state, string? description)
@@ -30,7 +34,6 @@ public static IAspect LoadAspect (Stream stream, IAspectState state, string? des
3034
} else {
3135
throw new NotImplementedException ();
3236
}
33-
ret.Read ();
3437

3538
return ret;
3639
}
@@ -62,7 +65,7 @@ public static IAspectState ProbeAspect (Stream stream, string? description)
6265
return new BasicAspectState (success: false);
6366
}
6467

65-
void Read ()
68+
void Read (AXMLParser? binaryParser)
6669
{
6770
if (binaryParser == null) {
6871
throw new NotImplementedException ();
@@ -76,6 +79,131 @@ void Read ()
7679
Log.Debug ($"'{Description}' loaded and parsed correctly.");
7780
}
7881

82+
static XmlNamespaceManager? PrepareForReading (XmlDocument? doc)
83+
{
84+
if (doc == null) {
85+
return null;
86+
}
87+
88+
var nsmgr = new XmlNamespaceManager (doc.NameTable);
89+
nsmgr.AddNamespace ("android", "http://schemas.android.com/apk/res/android");
90+
return nsmgr;
91+
}
92+
93+
static bool ValidXmlContext (XmlDocument? doc, XmlNamespaceManager? nsmgr, out XmlElement? root)
94+
{
95+
root = null;
96+
if (doc == null || nsmgr == null) {
97+
return false;
98+
}
99+
100+
root = doc.DocumentElement;
101+
return root != null;
102+
}
103+
104+
static string? TryGetMainActivity (XmlDocument? doc, XmlNamespaceManager? nsmgr)
105+
{
106+
if (!ValidXmlContext (doc, nsmgr, out XmlElement? root)) {
107+
return null;
108+
}
109+
110+
XmlNodeList? activities = root!.SelectNodes ("//manifest/application/activity", nsmgr!);
111+
if (activities == null || activities.Count == 0) {
112+
return null;
113+
}
114+
115+
foreach (XmlNode activity in activities) {
116+
string? name = GetLauncherActivityName (activity);
117+
if (name == null) {
118+
continue;
119+
}
120+
121+
return name;
122+
}
123+
124+
return null;
125+
126+
string? GetLauncherActivityName (XmlNode activity)
127+
{
128+
XmlNodeList? intentFilters = activity.SelectNodes ("./intent-filter", nsmgr!);
129+
if (intentFilters == null || intentFilters.Count == 0) {
130+
return null;
131+
}
132+
133+
bool isMain = false;
134+
foreach (XmlNode intentFilter in intentFilters) {
135+
XmlNodeList? actions = activity.SelectNodes ("./action", nsmgr!);
136+
if (actions == null || actions.Count == 0) {
137+
continue;
138+
}
139+
140+
if (!HaveNodeWithNameAttribute (actions, "android.intent.action.MAIN")) {
141+
continue;
142+
}
143+
144+
XmlNodeList? categories = activity.SelectNodes ("./category", nsmgr!);
145+
if (categories == null || categories.Count == 0) {
146+
continue;
147+
}
148+
149+
if (!HaveNodeWithNameAttribute (categories, "android.intent.category.LAUNCHER")) {
150+
continue;
151+
}
152+
153+
isMain = true;
154+
break;
155+
}
156+
157+
if (!isMain) {
158+
return null;
159+
}
160+
161+
var attr = activity.Attributes?.GetNamedItem ("android:name");
162+
if (attr == null) {
163+
return null;
164+
}
165+
166+
return attr.Value;
167+
}
168+
169+
bool HaveNodeWithNameAttribute (XmlNodeList list, string nameValue)
170+
{
171+
foreach (XmlNode? node in list) {
172+
var attr = node?.Attributes?.GetNamedItem ("android:name");
173+
if (attr == null || String.IsNullOrEmpty (attr.Value)) {
174+
continue;
175+
}
176+
177+
if (attr.Value == nameValue) {
178+
return true;
179+
}
180+
}
181+
182+
return false;
183+
}
184+
}
185+
186+
static string? TryGetPackageName (XmlDocument? doc, XmlNamespaceManager? nsmgr)
187+
{
188+
Log.Debug ("Trying to read package name");
189+
if (!ValidXmlContext (doc, nsmgr, out XmlElement? root) || root == null) {
190+
return null;
191+
}
192+
193+
XmlNode? manifest = root.SelectSingleNode ("//manifest", nsmgr);
194+
if (manifest == null || manifest.Attributes == null) {
195+
Log.Debug ("`manifest` element not found or it has no attributes");
196+
return null;
197+
}
198+
199+
XmlNode? package = manifest.Attributes.GetNamedItem ("package");
200+
if (package == null) {
201+
Log.Debug ("`package` attribute in the `manifest` element not found");
202+
}
203+
204+
return package == null ? null : package.Value;
205+
}
206+
79207
static XmlDocument ParsePlainXML (Stream stream)
80208
{
81209
stream.Seek (0, SeekOrigin.Begin);

tools/apput/src/Package/ApplicationPackage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ void TryLoadAndroidManifest ()
312312
return;
313313
}
314314
manifest = (AndroidManifest)AndroidManifest.LoadAspect (manifestStream, manifestState, AndroidManifestPath);
315+
PackageName = manifest.PackageName ?? String.Empty;
315316
} catch (Exception ex) {
316317
Log.Debug ($"Failed to load android manifest '{AndroidManifestPath}' from the archive.", ex);
317318
}

tools/apput/src/Reporters/ApplicationPackageReporter.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ public ApplicationPackageReporter (ApplicationPackage package)
1717

1818
protected override void DoReport ()
1919
{
20-
throw new System.NotImplementedException ();
20+
WriteAspectDesc (package.PackageFormat);
21+
22+
WriteSubsectionBanner ("Generic Android application information");
23+
WriteNativeArch (package.Architectures);
24+
WriteItem ("Package name", ValueOrNone (package.PackageName));
25+
WriteItem ("Main activity", ValueOrNone (package.MainActivity));
26+
WriteYesNo ("Valid Android package", package.ValidAndroidPackage);
27+
WriteYesNo ("Signed", package.Signed);
28+
WriteYesNo ("Debuggable", package.Debuggable);
29+
30+
WriteSubsectionBanner (".NET for Android application information");
31+
WriteItem ("Runtime", package.Runtime.ToString ());
2132
}
2233
}

tools/apput/src/Reporters/BaseReporter.cs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
using Xamarin.Android.Tools;
26

37
namespace ApplicationUtility;
48

59
abstract class BaseReporter : IReporter
610
{
7-
protected const ConsoleColor LabelColor = ConsoleColor.White;
11+
const string NativeArchitectureLabel = "Native target architecture";
12+
const string NativeArchitecturesLabel = NativeArchitectureLabel + "s";
13+
14+
protected const ConsoleColor LabelColor = ConsoleColor.Gray;
815
protected const ConsoleColor ValidValueColor = ConsoleColor.Green;
916
protected const ConsoleColor InvalidValueColor = ConsoleColor.Red;
1017
protected const ConsoleColor BannerColor = ConsoleColor.Cyan;
@@ -21,16 +28,50 @@ public void Report ()
2128

2229
protected abstract void DoReport ();
2330

31+
protected void WriteSubsectionBanner (string text)
32+
{
33+
WriteLine ();
34+
WriteLine (BannerColor, $"## {text}");
35+
}
36+
2437
protected void WriteAspectDesc (string text)
2538
{
2639
WriteItem ("Aspect type", text);
2740
}
2841

2942
protected void WriteNativeArch (NativeArchitecture arch)
3043
{
31-
WriteItem ("Native target architecture", arch.ToString ());
44+
WriteItem (NativeArchitectureLabel, arch.ToString ());
3245
}
3346

47+
protected void WriteNativeArch (AndroidTargetArch arch)
48+
{
49+
WriteItem (NativeArchitectureLabel, arch.ToString ());
50+
}
51+
52+
protected void WriteNativeArch (ICollection<AndroidTargetArch> arches)
53+
{
54+
if (arches.Count == 1) {
55+
WriteNativeArch (arches.First ());
56+
return;
57+
}
58+
59+
WriteLabel (NativeArchitecturesLabel);
60+
if (arches.Count == 0) {
61+
WriteLine (InvalidValueColor, "none");
62+
return;
63+
}
64+
65+
var architectures = new List<string> ();
66+
foreach (AndroidTargetArch arch in arches) {
67+
architectures.Add (arch.ToString ());
68+
}
69+
70+
WriteLine (ValidValueColor, String.Join (", ", architectures));
71+
}
72+
73+
protected void WriteYesNo (string label, bool value) => WriteItem (label, YesNo (value));
74+
3475
protected void WriteLabel (string label)
3576
{
3677
Write (LabelColor, $"{label}: ");

0 commit comments

Comments
 (0)