Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 1cfb4f4

Browse files
authored
[generator] Add support for emitting [UnsupportedOSPlatform] (#1307)
Fixes: #1309 Context: dotnet/android@944d306 Sometimes, Android *removes* APIs. For example, API-23 *removed* the `android.Manifest.permission.CLEAR_APP_USER_DATA` field. We still list this field, though: [`Android.Manifest.Permission.ClearAppUserData` field][0]. > [Android.Runtime.Register("CLEAR_APP_USER_DATA")] > public const string ClearAppUserData; In dotnet/android@944d3061 we updated `api-merge` to emit `//*/@#removed-since`. Consider this snippet from [`android/src/Mono.Android/Profiles/api-35.xml`][1]: <field deprecated="not deprecated" final="true" name="CLEAR_APP_USER_DATA" jni-signature="Ljava/lang/String;" static="true" transient="false" type="java.lang.String" type-generic-aware="java.lang.String" value="&quot;android.permission.CLEAR_APP_USER_DATA&quot;" visibility="public" volatile="false" removed-since="23" /> Modify `generator` to consume this information and emit `[UnsupportedOSPlatformAttribute ("androidXX.0")]` as needed for API that has been removed from `Mono.Android`. This would update `Android.Manifest.Permission.ClearAppUserData` to be: [Android.Runtime.Register("CLEAR_APP_USER_DATA")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute ("android23.0")] public const string ClearAppUserData; [0]: https://learn.microsoft.com/en-us/dotnet/api/android.manifest.permission.clearappuserdata?view=net-android-34.0 [1]: https://github.com/dotnet/android/blob/d012fb811b4bcebe260400f5fc7a0287108336d7/src/Mono.Android/Profiles/api-35.xml
1 parent f30e420 commit 1cfb4f4

9 files changed

Lines changed: 87 additions & 4 deletions

File tree

‎tests/generator-Tests/Unit-Tests/CodeGeneratorTests.cs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,36 @@ public void SupportedOSPlatformConstFields ()
14061406
StringAssert.Contains("[global::System.Runtime.Versioning.SupportedOSPlatformAttribute (\"android30.0\")]",builder.ToString(),"Should contain SupportedOSPlatform!");
14071407
}
14081408

1409+
[Test]
1410+
publicvoidUnsupportedOSPlatform()
1411+
{
1412+
varklass=SupportTypeBuilder.CreateClass("java.code.MyClass",options);
1413+
klass.ApiRemovedSince=30;
1414+
1415+
generator.Context.ContextTypes.Push(klass);
1416+
generator.WriteType(klass,string.Empty,newGenerationInfo("","","MyAssembly"));
1417+
generator.Context.ContextTypes.Pop();
1418+
1419+
StringAssert.Contains("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]",builder.ToString(),"Should contain UnsupportedOSPlatform!");
1420+
}
1421+
1422+
[Test]
1423+
publicvoidUnsupportedOSPlatformConstFields()
1424+
{
1425+
varklass=newTestClass("java.lang.Object","com.mypackage.foo");
1426+
varfield=newTestField("java.lang.String","bar").SetConstant("MY_VALUE");
1427+
1428+
field.ApiRemovedSince=30;
1429+
1430+
klass.Fields.Add(field);
1431+
1432+
generator.Context.ContextTypes.Push(klass);
1433+
generator.WriteType(klass,string.Empty,newGenerationInfo("","","MyAssembly"));
1434+
generator.Context.ContextTypes.Pop();
1435+
1436+
StringAssert.Contains("[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android30.0\")]",builder.ToString(),"Should contain UnsupportedOSPlatform!");
1437+
}
1438+
14091439
[Test]
14101440
publicvoidStringPropertyOverride([Values("true","false")]stringfinal)
14111441
{

‎tests/generator-Tests/Unit-Tests/XmlApiImporterTests.cs‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ public void CreateClass_CorrectApiSinceOverridePackage ()
5151
Assert.AreEqual(9,klass.ApiAvailableSince);
5252
}
5353

54+
[Test]
55+
publicvoidCreateClass_CorrectApiRemoved()
56+
{
57+
varxml=XDocument.Parse("<package name='com.example.test' jni-name='com/example/test'><class name='myclass' removed-since='7' /></package>");
58+
varklass=XmlApiImporter.CreateClass(xml.Root,xml.Root.Element("class"),opt);
59+
60+
Assert.AreEqual(7,klass.ApiRemovedSince);
61+
}
62+
5463
[Test]
5564
publicvoidCreateCtor_EnsureValidName()
5665
{

‎tools/generator/ApiVersionsSupport.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static class ApiVersionsSupport
1818
publicinterfaceIApiAvailability
1919
{
2020
intApiAvailableSince{get;set;}
21+
intApiRemovedSince{get;set;}
2122
}
2223

2324
staticIEnumerable<GenBase>FlattenGens(IEnumerable<GenBase>gens)

‎tools/generator/Java.Interop.Tools.Generator.Importers/XmlApiImporter.cs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public static Ctor CreateCtor (GenBase declaringType, XElement elem, CodeGenerat
159159
varctor=newCtor(declaringType){
160160
AnnotatedVisibility=elem.XGetAttribute("annotated-visibility"),
161161
ApiAvailableSince=declaringType.ApiAvailableSince,
162+
ApiRemovedSince=declaringType.ApiRemovedSince,
162163
CustomAttributes=elem.XGetAttribute("customAttributes"),
163164
Deprecated=elem.Deprecated(),
164165
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
@@ -211,6 +212,7 @@ public static Field CreateField (GenBase declaringType, XElement elem, CodeGener
211212
varfield=newField{
212213
AnnotatedVisibility=elem.XGetAttribute("annotated-visibility"),
213214
ApiAvailableSince=declaringType.ApiAvailableSince,
215+
ApiRemovedSince=declaringType.ApiRemovedSince,
214216
DeprecatedComment=elem.XGetAttribute("deprecated"),
215217
DeprecatedSince=elem.XGetAttributeAsIntOrNull("deprecated-since"),
216218
IsAcw=true,
@@ -369,6 +371,7 @@ public static Method CreateMethod (GenBase declaringType, XElement elem, CodeGen
369371
varmethod=newMethod(declaringType){
370372
AnnotatedVisibility=elem.XGetAttribute("annotated-visibility"),
371373
ApiAvailableSince=declaringType.ApiAvailableSince,
374+
ApiRemovedSince=declaringType.ApiRemovedSince,
372375
ArgsType=elem.Attribute("argsType")?.Value,
373376
CustomAttributes=elem.XGetAttribute("customAttributes"),
374377
Deprecated=elem.Deprecated(),
@@ -515,9 +518,12 @@ static XElement GetPreviousClass (XNode n, string nameValue)
515518
// Elements need to be passed in the above order. (package, class, member)
516519
staticvoidFillApiSince(ApiVersionsSupport.IApiAvailabilitymodel,paramsXElement[]elems)
517520
{
518-
foreach(vareleminelems)
521+
foreach(vareleminelems){
519522
if(int.TryParse(elem.XGetAttribute("api-since"),outvarresult))
520523
model.ApiAvailableSince=result;
524+
if(int.TryParse(elem.XGetAttribute("removed-since"),outvarremoved))
525+
model.ApiRemovedSince=removed;
526+
}
521527
}
522528

523529
staticboolIsObfuscatedName(intthreshold,stringname)

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/Field.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Field : ApiVersionsSupport.IApiAvailability, ISourceLineInfo
99
publicstringAnnotatedVisibility{get;set;}
1010
publicstringAnnotation{get;set;}
1111
publicintApiAvailableSince{get;set;}
12+
publicintApiRemovedSince{get;set;}
1213
publicstringDeprecatedComment{get;set;}
1314
publicint?DeprecatedSince{get;set;}
1415
publicboolIsAcw{get;set;}

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/GenBase.cs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ IEnumerable<GenBase> Ancestors ()
188188

189189
publicintApiAvailableSince{get;set;}
190190

191+
publicintApiRemovedSince{get;set;}
192+
191193
publicvirtualClassGenBaseGen=>null;
192194

193195
publicGenBaseBaseSymbol=>

‎tools/generator/Java.Interop.Tools.Generator.ObjectModel/MethodBase.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ protected MethodBase (GenBase declaringType)
1616
publicstringAnnotatedVisibility{get;set;}
1717
publicstringAnnotation{get;internalset;}
1818
publicintApiAvailableSince{get;set;}
19+
publicintApiRemovedSince{get;set;}
1920
publicstringAssemblyName{get;set;}
2021
publicGenBaseDeclaringType{get;}
2122
publicstringDeprecated{get;set;}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Linq;
4+
usingSystem.Text;
5+
usingSystem.Threading.Tasks;
6+
usingXamarin.SourceWriter;
7+
8+
namespacegenerator.SourceWriters
9+
{
10+
publicclassUnsupportedOSPlatformAttr:AttributeWriter
11+
{
12+
publicintVersion{get;}
13+
14+
publicUnsupportedOSPlatformAttr(intversion)=>Version=version;
15+
16+
publicoverridevoidWriteAttribute(CodeWriterwriter)
17+
{
18+
writer.WriteLine($"[global::System.Runtime.Versioning.UnsupportedOSPlatformAttribute (\"android{Version}.0\")]");
19+
}
20+
}
21+
}

‎tools/generator/SourceWriters/Extensions/SourceWriterExtensions.cs‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace generator.SourceWriters
1212
{
1313
publicstaticclassSourceWriterExtensions
1414
{
15+
constintMINIMUM_API_LEVEL=21;
16+
1517
publicstaticvoidAddField(TypeWritertw,GenBasetype,Fieldfield,CodeGenerationOptionsopt)
1618
{
1719
if(field.NeedsProperty)
@@ -306,16 +308,26 @@ public static void AddParameterListCallArgs (List<string> body, ParameterList pa
306308
}
307309

308310
publicstaticvoidAddSupportedOSPlatform(List<AttributeWriter>attributes,ApiVersionsSupport.IApiAvailabilitymember,CodeGenerationOptionsopt)
309-
=>AddSupportedOSPlatform(attributes,member.ApiAvailableSince,opt);
311+
{
312+
AddSupportedOSPlatform(attributes,member.ApiAvailableSince,opt);
313+
AddUnsupportedOSPlatform(attributes,member.ApiRemovedSince,opt);
314+
}
310315

311316
publicstaticvoidAddSupportedOSPlatform(List<AttributeWriter>attributes,intsince,CodeGenerationOptionsopt)
312317
{
313318
// There's no sense in writing say 'android15' because we do not support older APIs,
314319
// so those APIs will be available in all of our versions.
315-
if(since>21&&opt.CodeGenerationTarget==Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
320+
if(since>MINIMUM_API_LEVEL&&opt.CodeGenerationTarget==Xamarin.Android.Binder.CodeGenerationTarget.XAJavaInterop1)
316321
attributes.Add(newSupportedOSPlatformAttr(since));
317322
}
318323

324+
publicstaticvoidAddUnsupportedOSPlatform(List<AttributeWriter>attributes,intsince,CodeGenerationOptionsopt)
325+
{
326+
// Here it makes sense to still write 'android15' because it will be missing in later versions like `android35`.
327+
if(since>0&&opt.CodeGenerationTarget==CodeGenerationTarget.XAJavaInterop1)
328+
attributes.Add(newUnsupportedOSPlatformAttr(since));
329+
}
330+
319331
publicstaticvoidAddObsolete(List<AttributeWriter>attributes,stringmessage,CodeGenerationOptionsopt,boolforceDeprecate=false,boolisError=false,int?deprecatedSince=null)
320332
{
321333
// Bail if we're not obsolete
@@ -336,7 +348,7 @@ static bool AddObsoletedOSPlatformAttribute (List<AttributeWriter> attributes, s
336348
returnfalse;
337349

338350
// If it was obsoleted in a version earlier than we support (like 15), use a regular [Obsolete] instead
339-
if(!deprecatedSince.HasValue||deprecatedSince<=21)
351+
if(!deprecatedSince.HasValue||deprecatedSince<=MINIMUM_API_LEVEL)
340352
returnfalse;
341353

342354
// This is the default Android message, but it isn't useful so remove it

0 commit comments

Comments
 (0)