diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.java index 3b773a8f322..13e446f8081 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/blob/BlobModule.java @@ -14,6 +14,8 @@ import android.provider.MediaStore; import android.webkit.MimeTypeMap; import androidx.annotation.Nullable; import com.facebook.fbreact.specs.NativeBlobModuleSpec; +import com.facebook.infer.annotation.Assertions; +import com.facebook.infer.annotation.Nullsafe; import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactApplicationContext; @@ -41,6 +43,7 @@ import okhttp3.RequestBody; import okhttp3.ResponseBody; import okio.ByteString; +@Nullsafe(Nullsafe.Mode.LOCAL) @ReactModule(name = NativeBlobModuleSpec.NAME) public class BlobModule extends NativeBlobModuleSpec { @@ -106,18 +109,21 @@ public class BlobModule extends NativeBlobModuleSpec { @Override public RequestBody toRequestBody(ReadableMap data, String contentType) { String type = contentType; - // NULLSAFE_FIXME[Nullable Dereference] - if (data.hasKey("type") && !data.getString("type").isEmpty()) { + if (data.hasKey("type") + && data.getString("type") != null + && !data.getString("type").isEmpty()) { type = data.getString("type"); } if (type == null) { type = "application/octet-stream"; } ReadableMap blob = data.getMap("blob"); - // NULLSAFE_FIXME[Nullable Dereference] - String blobId = blob.getString("blobId"); - // NULLSAFE_FIXME[Parameter Not Nullable, Nullable Dereference] - byte[] bytes = resolve(blobId, blob.getInt("offset"), blob.getInt("size")); + + // supports() ensures blob is not null, but for nullability we have to check it + Assertions.assertNotNull(blob, "Blob is null even though supports() returned true"); + + byte[] bytes = + resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size")); return RequestBody.create(MediaType.parse(type), bytes); } @@ -151,8 +157,7 @@ public class BlobModule extends NativeBlobModuleSpec { } @Override - // NULLSAFE_FIXME[Inconsistent Subclass Return Annotation] - public @Nullable Map getTypedExportedConstants() { + public Map getTypedExportedConstants() { // The application can register BlobProvider as a ContentProvider so that blobs are resolvable. // If it does, it needs to tell us what authority was used via this string resource. Resources resources = getReactApplicationContext().getResources(); @@ -205,11 +210,10 @@ public class BlobModule extends NativeBlobModuleSpec { if (sizeParam != null) { size = Integer.parseInt(sizeParam, 10); } - // NULLSAFE_FIXME[Parameter Not Nullable] return resolve(blobId, offset, size); } - public @Nullable byte[] resolve(String blobId, int offset, int size) { + public @Nullable byte[] resolve(@Nullable String blobId, int offset, int size) { synchronized (mBlobs) { byte[] data = mBlobs.get(blobId); if (data == null) { @@ -226,7 +230,6 @@ public class BlobModule extends NativeBlobModuleSpec { } public @Nullable byte[] resolve(ReadableMap blob) { - // NULLSAFE_FIXME[Parameter Not Nullable] return resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size")); } @@ -273,9 +276,8 @@ public class BlobModule extends NativeBlobModuleSpec { } } - private String getNameFromUri(Uri contentUri) { + private @Nullable String getNameFromUri(Uri contentUri) { if ("file".equals(contentUri.getScheme())) { - // NULLSAFE_FIXME[Return Not Nullable] return contentUri.getLastPathSegment(); } String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; @@ -286,14 +288,12 @@ public class BlobModule extends NativeBlobModuleSpec { if (metaCursor != null) { try { if (metaCursor.moveToFirst()) { - // NULLSAFE_FIXME[Return Not Nullable] return metaCursor.getString(0); } } finally { metaCursor.close(); } } - // NULLSAFE_FIXME[Return Not Nullable] return contentUri.getLastPathSegment(); } @@ -321,15 +321,13 @@ public class BlobModule extends NativeBlobModuleSpec { return type; } - private WebSocketModule getWebSocketModule(String reason) { + private @Nullable WebSocketModule getWebSocketModule(String reason) { ReactApplicationContext reactApplicationContext = getReactApplicationContextIfActiveOrWarn(); if (reactApplicationContext != null) { - // NULLSAFE_FIXME[Return Not Nullable] return reactApplicationContext.getNativeModule(WebSocketModule.class); } - // NULLSAFE_FIXME[Return Not Nullable] return null; } @@ -340,11 +338,9 @@ public class BlobModule extends NativeBlobModuleSpec { if (reactApplicationContext != null) { NetworkingModule networkingModule = reactApplicationContext.getNativeModule(NetworkingModule.class); - // NULLSAFE_FIXME[Nullable Dereference] + Assertions.assertNotNull(networkingModule, "NetworkingModule is null"); networkingModule.addUriHandler(mNetworkingUriHandler); - // NULLSAFE_FIXME[Nullable Dereference] networkingModule.addRequestBodyHandler(mNetworkingRequestBodyHandler); - // NULLSAFE_FIXME[Nullable Dereference] networkingModule.addResponseHandler(mNetworkingResponseHandler); } } @@ -378,14 +374,10 @@ public class BlobModule extends NativeBlobModuleSpec { WebSocketModule webSocketModule = getWebSocketModule("sendOverSocket"); if (webSocketModule != null) { - // NULLSAFE_FIXME[Parameter Not Nullable] byte[] data = resolve(blob.getString("blobId"), blob.getInt("offset"), blob.getInt("size")); if (data != null) { webSocketModule.sendBinary(ByteString.of(data), id); - } else { - // NULLSAFE_FIXME[Parameter Not Nullable] - webSocketModule.sendBinary((ByteString) null, id); } } } @@ -396,24 +388,24 @@ public class BlobModule extends NativeBlobModuleSpec { ArrayList partList = new ArrayList<>(parts.size()); for (int i = 0; i < parts.size(); i++) { ReadableMap part = parts.getMap(i); - // NULLSAFE_FIXME[Nullable Dereference] - switch (part.getString("type")) { + Assertions.assertNotNull(part, "Blob part is null"); + String type = part.getString("type"); + Assertions.assertNotNull(type, "Invalid type for blob: null"); + switch (type) { case "blob": - // NULLSAFE_FIXME[Nullable Dereference] ReadableMap blob = part.getMap("data"); - // NULLSAFE_FIXME[Nullable Dereference] + Assertions.assertNotNull(blob, "Blob is null"); totalBlobSize += blob.getInt("size"); - // NULLSAFE_FIXME[Parameter Not Nullable] partList.add(i, resolve(blob)); break; case "string": - // NULLSAFE_FIXME[Nullable Dereference] - byte[] bytes = part.getString("data").getBytes(Charset.forName("UTF-8")); + String data = part.getString("data"); + Assertions.assertNotNull(data, "Data is null"); + byte[] bytes = data.getBytes(Charset.forName("UTF-8")); totalBlobSize += bytes.length; partList.add(i, bytes); break; default: - // NULLSAFE_FIXME[Nullable Dereference] throw new IllegalArgumentException("Invalid type for blob: " + part.getString("type")); } }