Skip to content

Commit f43629c

Browse files
committed
Add context, settings, and JSON APIs
Introduce C2PAContext and C2PASettings for shared configuration across readers and builders. Add C2PAJson for centralized JSON serialization. Add SettingsValidator for validating C2PA settings. Rewrite Builder to use context-based creation flow. Add Reader context support with withStream and withFragment. Update native library to v0.75.19.
1 parent c4ddab3 commit f43629c

16 files changed

Lines changed: 2427 additions & 384 deletions

File tree

library/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ android {
3636

3737
// Specify ABIs to use prebuilt .so files
3838
ndk {
39-
abiFilters.add("x86_64")
4039
abiFilters.add("arm64-v8a")
4140
abiFilters.add("armeabi-v7a")
4241
abiFilters.add("x86")
42+
abiFilters.add("x86_64")
4343
}
4444
}
4545

library/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# C2PA Native Library Version
22
# Update this to use a different release from https://github.com/contentauth/c2pa-rs/releases
3-
c2paVersion=v0.75.8
3+
c2paVersion=v0.75.19

library/src/androidTest/kotlin/org/contentauth/c2pa/AndroidBuilderTests.kt

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
This file is licensed to you under the Apache License, Version 2.0
33
(http://www.apache.org/licenses/LICENSE-2.0) or the MIT license
44
(http://opensource.org/licenses/MIT), at your option.
@@ -9,6 +9,7 @@ ANY KIND, either express or implied. See the LICENSE-MIT and LICENSE-APACHE
99
files for the specific language governing permissions and limitations under
1010
each license.
1111
*/
12+
1213
package org.contentauth.c2pa
1314

1415
import android.content.Context
@@ -85,4 +86,52 @@ class AndroidBuilderTests : BuilderTests() {
8586
val result = testJsonRoundTrip()
8687
assertTrue(result.success, "JSON Round-trip test failed: ${result.message}")
8788
}
89+
90+
@Test
91+
fun runTestBuilderFromContextWithSettings() = runBlocking {
92+
val result = testBuilderFromContextWithSettings()
93+
assertTrue(result.success, "Builder from Context with Settings test failed: ${result.message}")
94+
}
95+
96+
@Test
97+
fun runTestBuilderFromJsonWithSettings() = runBlocking {
98+
val result = testBuilderFromJsonWithSettings()
99+
assertTrue(result.success, "Builder fromJson with Settings test failed: ${result.message}")
100+
}
101+
102+
@Test
103+
fun runTestBuilderWithArchive() = runBlocking {
104+
val result = testBuilderWithArchive()
105+
assertTrue(result.success, "Builder withArchive test failed: ${result.message}")
106+
}
107+
108+
@Test
109+
fun runTestReaderFromContext() = runBlocking {
110+
val result = testReaderFromContext()
111+
assertTrue(result.success, "Reader fromContext test failed: ${result.message}")
112+
}
113+
114+
@Test
115+
fun runTestBuilderSetIntent() = runBlocking {
116+
val result = testBuilderSetIntent()
117+
assertTrue(result.success, "Builder Set Intent test failed: ${result.message}")
118+
}
119+
120+
@Test
121+
fun runTestBuilderAddAction() = runBlocking {
122+
val result = testBuilderAddAction()
123+
assertTrue(result.success, "Builder Add Action test failed: ${result.message}")
124+
}
125+
126+
@Test
127+
fun runTestSettingsSetValue() = runBlocking {
128+
val result = testSettingsSetValue()
129+
assertTrue(result.success, "C2PASettings setValue test failed: ${result.message}")
130+
}
131+
132+
@Test
133+
fun runTestBuilderIntentEditAndUpdate() = runBlocking {
134+
val result = testBuilderIntentEditAndUpdate()
135+
assertTrue(result.success, "Builder Intent Edit and Update test failed: ${result.message}")
136+
}
88137
}

library/src/main/jni/c2pa_jni.c

Lines changed: 241 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -750,29 +750,6 @@ JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Reader_resourceToStreamNative(
750750
}
751751

752752
// Builder native methods
753-
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Builder_nativeFromJson(JNIEnv *env, jclass clazz, jstring manifestJson) {
754-
if (manifestJson == NULL) {
755-
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
756-
"Manifest JSON cannot be null");
757-
return 0;
758-
}
759-
760-
const char *cmanifestJson = jstring_to_cstring(env, manifestJson);
761-
if (cmanifestJson == NULL) {
762-
return 0;
763-
}
764-
765-
struct C2paBuilder *builder = c2pa_builder_from_json(cmanifestJson);
766-
release_cstring(env, manifestJson, cmanifestJson);
767-
768-
if (builder == NULL) {
769-
throw_c2pa_exception(env, "Failed to create builder from JSON");
770-
return 0;
771-
}
772-
773-
return (jlong)(uintptr_t)builder;
774-
}
775-
776753
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Builder_nativeFromArchive(JNIEnv *env, jclass clazz, jlong streamPtr) {
777754
if (streamPtr == 0) {
778755
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
@@ -1216,6 +1193,247 @@ JNIEXPORT void JNICALL Java_org_contentauth_c2pa_Signer_free(JNIEnv *env, jobjec
12161193
}
12171194
}
12181195

1196+
// C2PASettings native methods
1197+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_C2PASettings_nativeNew(JNIEnv *env, jclass clazz) {
1198+
struct C2paSettings *settings = c2pa_settings_new();
1199+
if (settings == NULL) {
1200+
return 0;
1201+
}
1202+
return (jlong)(uintptr_t)settings;
1203+
}
1204+
1205+
JNIEXPORT jint JNICALL Java_org_contentauth_c2pa_C2PASettings_updateFromStringNative(JNIEnv *env, jobject obj, jlong settingsPtr, jstring settingsStr, jstring format) {
1206+
if (settingsPtr == 0 || settingsStr == NULL || format == NULL) {
1207+
return -1;
1208+
}
1209+
1210+
struct C2paSettings *settings = (struct C2paSettings*)(uintptr_t)settingsPtr;
1211+
const char *csettingsStr = jstring_to_cstring(env, settingsStr);
1212+
const char *cformat = jstring_to_cstring(env, format);
1213+
1214+
if (csettingsStr == NULL || cformat == NULL) {
1215+
release_cstring(env, settingsStr, csettingsStr);
1216+
release_cstring(env, format, cformat);
1217+
return -1;
1218+
}
1219+
1220+
int result = c2pa_settings_update_from_string(settings, csettingsStr, cformat);
1221+
1222+
release_cstring(env, settingsStr, csettingsStr);
1223+
release_cstring(env, format, cformat);
1224+
1225+
return result;
1226+
}
1227+
1228+
JNIEXPORT jint JNICALL Java_org_contentauth_c2pa_C2PASettings_setValueNative(JNIEnv *env, jobject obj, jlong settingsPtr, jstring path, jstring value) {
1229+
if (settingsPtr == 0 || path == NULL || value == NULL) {
1230+
return -1;
1231+
}
1232+
1233+
struct C2paSettings *settings = (struct C2paSettings*)(uintptr_t)settingsPtr;
1234+
const char *cpath = jstring_to_cstring(env, path);
1235+
const char *cvalue = jstring_to_cstring(env, value);
1236+
1237+
if (cpath == NULL || cvalue == NULL) {
1238+
release_cstring(env, path, cpath);
1239+
release_cstring(env, value, cvalue);
1240+
return -1;
1241+
}
1242+
1243+
int result = c2pa_settings_set_value(settings, cpath, cvalue);
1244+
1245+
release_cstring(env, path, cpath);
1246+
release_cstring(env, value, cvalue);
1247+
1248+
return result;
1249+
}
1250+
1251+
JNIEXPORT void JNICALL Java_org_contentauth_c2pa_C2PASettings_free(JNIEnv *env, jobject obj, jlong settingsPtr) {
1252+
if (settingsPtr != 0) {
1253+
c2pa_free((const void*)(uintptr_t)settingsPtr);
1254+
}
1255+
}
1256+
1257+
// C2PAContext native methods
1258+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_C2PAContext_nativeNew(JNIEnv *env, jclass clazz) {
1259+
struct C2paContext *context = c2pa_context_new();
1260+
if (context == NULL) {
1261+
return 0;
1262+
}
1263+
return (jlong)(uintptr_t)context;
1264+
}
1265+
1266+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_C2PAContext_nativeNewWithSettings(JNIEnv *env, jclass clazz, jlong settingsPtr) {
1267+
if (settingsPtr == 0) {
1268+
return 0;
1269+
}
1270+
1271+
struct C2paContextBuilder *builder = c2pa_context_builder_new();
1272+
if (builder == NULL) {
1273+
return 0;
1274+
}
1275+
1276+
struct C2paSettings *settings = (struct C2paSettings*)(uintptr_t)settingsPtr;
1277+
int result = c2pa_context_builder_set_settings(builder, settings);
1278+
if (result < 0) {
1279+
c2pa_free(builder);
1280+
return 0;
1281+
}
1282+
1283+
// build consumes the builder
1284+
struct C2paContext *context = c2pa_context_builder_build(builder);
1285+
if (context == NULL) {
1286+
return 0;
1287+
}
1288+
1289+
return (jlong)(uintptr_t)context;
1290+
}
1291+
1292+
JNIEXPORT void JNICALL Java_org_contentauth_c2pa_C2PAContext_free(JNIEnv *env, jobject obj, jlong contextPtr) {
1293+
if (contextPtr != 0) {
1294+
c2pa_free((const void*)(uintptr_t)contextPtr);
1295+
}
1296+
}
1297+
1298+
// Builder context-based methods
1299+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Builder_nativeFromContext(JNIEnv *env, jclass clazz, jlong contextPtr) {
1300+
if (contextPtr == 0) {
1301+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1302+
"Context cannot be null");
1303+
return 0;
1304+
}
1305+
1306+
struct C2paContext *context = (struct C2paContext*)(uintptr_t)contextPtr;
1307+
struct C2paBuilder *builder = c2pa_builder_from_context(context);
1308+
1309+
if (builder == NULL) {
1310+
throw_c2pa_exception(env, "Failed to create builder from context");
1311+
return 0;
1312+
}
1313+
1314+
return (jlong)(uintptr_t)builder;
1315+
}
1316+
1317+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Builder_withDefinitionNative(JNIEnv *env, jobject obj, jlong builderPtr, jstring manifestJson) {
1318+
if (builderPtr == 0 || manifestJson == NULL) {
1319+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1320+
"Builder and manifest JSON cannot be null");
1321+
return 0;
1322+
}
1323+
1324+
struct C2paBuilder *builder = (struct C2paBuilder*)(uintptr_t)builderPtr;
1325+
const char *cmanifestJson = jstring_to_cstring(env, manifestJson);
1326+
if (cmanifestJson == NULL) {
1327+
return 0;
1328+
}
1329+
1330+
// This consumes the old builder pointer
1331+
struct C2paBuilder *newBuilder = c2pa_builder_with_definition(builder, cmanifestJson);
1332+
release_cstring(env, manifestJson, cmanifestJson);
1333+
1334+
if (newBuilder == NULL) {
1335+
throw_c2pa_exception(env, "Failed to set builder definition");
1336+
return 0;
1337+
}
1338+
1339+
return (jlong)(uintptr_t)newBuilder;
1340+
}
1341+
1342+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Builder_withArchiveNative(JNIEnv *env, jobject obj, jlong builderPtr, jlong streamPtr) {
1343+
if (builderPtr == 0 || streamPtr == 0) {
1344+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1345+
"Builder and stream cannot be null");
1346+
return 0;
1347+
}
1348+
1349+
struct C2paBuilder *builder = (struct C2paBuilder*)(uintptr_t)builderPtr;
1350+
struct C2paStream *stream = (struct C2paStream*)(uintptr_t)streamPtr;
1351+
1352+
// This consumes the old builder pointer
1353+
struct C2paBuilder *newBuilder = c2pa_builder_with_archive(builder, stream);
1354+
1355+
if (newBuilder == NULL) {
1356+
throw_c2pa_exception(env, "Failed to set builder archive");
1357+
return 0;
1358+
}
1359+
1360+
return (jlong)(uintptr_t)newBuilder;
1361+
}
1362+
1363+
// Reader context-based methods
1364+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Reader_nativeFromContext(JNIEnv *env, jclass clazz, jlong contextPtr) {
1365+
if (contextPtr == 0) {
1366+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1367+
"Context cannot be null");
1368+
return 0;
1369+
}
1370+
1371+
struct C2paContext *context = (struct C2paContext*)(uintptr_t)contextPtr;
1372+
struct C2paReader *reader = c2pa_reader_from_context(context);
1373+
1374+
if (reader == NULL) {
1375+
throw_c2pa_exception(env, "Failed to create reader from context");
1376+
return 0;
1377+
}
1378+
1379+
return (jlong)(uintptr_t)reader;
1380+
}
1381+
1382+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Reader_withStreamNative(JNIEnv *env, jobject obj, jlong readerPtr, jstring format, jlong streamPtr) {
1383+
if (readerPtr == 0 || format == NULL || streamPtr == 0) {
1384+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1385+
"Reader, format, and stream cannot be null");
1386+
return 0;
1387+
}
1388+
1389+
struct C2paReader *reader = (struct C2paReader*)(uintptr_t)readerPtr;
1390+
const char *cformat = jstring_to_cstring(env, format);
1391+
if (cformat == NULL) {
1392+
return 0;
1393+
}
1394+
1395+
struct C2paStream *stream = (struct C2paStream*)(uintptr_t)streamPtr;
1396+
1397+
// This consumes the old reader pointer
1398+
struct C2paReader *newReader = c2pa_reader_with_stream(reader, cformat, stream);
1399+
release_cstring(env, format, cformat);
1400+
1401+
if (newReader == NULL) {
1402+
throw_c2pa_exception(env, "Failed to configure reader with stream");
1403+
return 0;
1404+
}
1405+
1406+
return (jlong)(uintptr_t)newReader;
1407+
}
1408+
1409+
JNIEXPORT jlong JNICALL Java_org_contentauth_c2pa_Reader_withFragmentNative(JNIEnv *env, jobject obj, jlong readerPtr, jstring format, jlong streamPtr, jlong fragmentPtr) {
1410+
if (readerPtr == 0 || format == NULL || streamPtr == 0 || fragmentPtr == 0) {
1411+
(*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/IllegalArgumentException"),
1412+
"Reader, format, stream, and fragment cannot be null");
1413+
return 0;
1414+
}
1415+
1416+
struct C2paReader *reader = (struct C2paReader*)(uintptr_t)readerPtr;
1417+
const char *cformat = jstring_to_cstring(env, format);
1418+
if (cformat == NULL) {
1419+
return 0;
1420+
}
1421+
1422+
struct C2paStream *stream = (struct C2paStream*)(uintptr_t)streamPtr;
1423+
struct C2paStream *fragment = (struct C2paStream*)(uintptr_t)fragmentPtr;
1424+
1425+
// This consumes the old reader pointer
1426+
struct C2paReader *newReader = c2pa_reader_with_fragment(reader, cformat, stream, fragment);
1427+
release_cstring(env, format, cformat);
1428+
1429+
if (newReader == NULL) {
1430+
throw_c2pa_exception(env, "Failed to configure reader with fragment");
1431+
return 0;
1432+
}
1433+
1434+
return (jlong)(uintptr_t)newReader;
1435+
}
1436+
12191437
// Ed25519 signing
12201438
JNIEXPORT jbyteArray JNICALL Java_org_contentauth_c2pa_C2PA_ed25519SignNative(JNIEnv *env, jclass clazz, jbyteArray data, jstring privateKey) {
12211439
if (data == NULL || privateKey == NULL) {

0 commit comments

Comments
 (0)