Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c69b1d4f8a | ||
|
|
74aa08a4b5 | ||
|
|
39fb9b5cb3 | ||
|
|
6bcf3de467 |
@@ -0,0 +1,3 @@
|
|||||||
|
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "Verify cross-layer numeric type alignment between Flutter Android bridge and SDK core bindings."}
|
||||||
|
{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "Check whether repetitive numeric forwarding patterns were fixed consistently and safely."}
|
||||||
|
{"file": ".trellis/tasks/05-12-audit-android-int-long-native-parameter-mismatches-against-sdk-core/prd.md", "reason": "Validation target for confirmed mismatch fixes and audit coverage."}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{"file": ".trellis/spec/guides/cross-layer-thinking-guide.md", "reason": "Audit Android Flutter bridge numeric boxing against SDK core signatures across Dart -> MethodChannel -> Java -> gomobile bindings."}
|
||||||
|
{"file": ".trellis/spec/guides/code-reuse-thinking-guide.md", "reason": "Look for repeated Android bridge numeric forwarding patterns that should use typed helpers consistently."}
|
||||||
|
{"file": ".trellis/tasks/05-12-audit-android-int-long-native-parameter-mismatches-against-sdk-core/prd.md", "reason": "Task scope, confirmed crash, signature findings, and acceptance criteria for the Android int/long mismatch audit."}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# audit android int-long native parameter mismatches against sdk core
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Audit the Flutter Android bridge managers against the bound SDK core signatures, find places where Flutter `int` values are forwarded with the wrong boxed Java type (`Integer` vs `Long` / `int32`-style expectations), and fix confirmed mismatch risks to prevent more runtime `ClassCastException` failures.
|
||||||
|
|
||||||
|
## What I already know
|
||||||
|
|
||||||
|
- A real crash was reproduced in `getChannelHistoryMessages` on Android: `java.lang.Integer cannot be cast to java.lang.Long`.
|
||||||
|
- The fixed plugin call site was `android/src/main/java/io/openim/flutter_openim_sdk/manager/ChannelManager.java`, where `count` was passed via `value(...)` instead of numeric conversion.
|
||||||
|
- Flutter side sends Dart `int` values through `MethodChannel`, which arrive on Android as boxed Java numbers.
|
||||||
|
- This repo’s Android bridge currently mixes raw `value(methodCall, key)` and `int2long(methodCall, key)` for numeric parameters.
|
||||||
|
- iOS already uses typed accessors like `methodCall[int:]`, `methodCall[int32:]`, `methodCall[int64:]`.
|
||||||
|
- SDK core source is available at `/mnt/d/workspace/go/im_dev/openim-sdk-core` for signature comparison.
|
||||||
|
- Core signature examples already checked:
|
||||||
|
- `GetChannelHistoryMessages(..., count int, sinceSeq int64)`
|
||||||
|
- `GetChannelMemberList(..., filter int32, offset int32, count int32)`
|
||||||
|
- `GetGroupMemberList(..., filter int32, offset int32, count int32)`
|
||||||
|
- `GetJoinedGroupListPage(..., offset, count int32)`
|
||||||
|
- `GetGroupMemberListByJoinTimeFilter(..., offset int32, count int32, joinTimeBegin int64, joinTimeEnd int64)`
|
||||||
|
- `GetFriendListPage(..., offset int32, count int32, filterBlack bool)`
|
||||||
|
- `GetConversationListSplit(..., offset int, count int)`
|
||||||
|
- `gomobile`/`gobind` Java bindings map Go `int` and `int64` to Java `long`, while `int32` maps to Java `int`; this is a common source of cross-platform bridge confusion.
|
||||||
|
|
||||||
|
## Assumptions (temporary)
|
||||||
|
|
||||||
|
- The Android binding layer should match the generated Java API’s expected boxed types exactly enough to avoid reflection/runtime cast failures.
|
||||||
|
- Some existing `value(...)` calls on numeric args are safe when the generated Java signature expects Java `int`/boxed `Integer`, but unsafe when it expects Java `long`/boxed `Long`.
|
||||||
|
- The task likely includes both audit and code fixes for confirmed risks, not just reporting.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- Should this task only fix confirmed Android mismatches found by comparing to SDK core/bound signatures, or also refactor the bridge toward explicit typed helpers (`int32` / `int64`) even for currently-safe call sites?
|
||||||
|
|
||||||
|
## Requirements (evolving)
|
||||||
|
|
||||||
|
- Compare Android manager numeric forwarding against SDK core signatures and binding behavior.
|
||||||
|
- Identify confirmed mismatch sites and distinguish them from safe `int32` call sites.
|
||||||
|
- Fix confirmed mismatch sites with minimal behavior change.
|
||||||
|
- Summarize findings clearly, including why each changed call needed conversion.
|
||||||
|
- If a broader pattern is found, capture it in spec/guides to prevent recurrence.
|
||||||
|
|
||||||
|
## Acceptance Criteria (evolving)
|
||||||
|
|
||||||
|
- [ ] All Android manager numeric parameters that should be passed as Java `Long` are identified and fixed.
|
||||||
|
- [ ] No confirmed `Integer` → `Long` mismatch remains in audited Android manager methods.
|
||||||
|
- [ ] Findings are backed by SDK core signature comparison.
|
||||||
|
- [ ] Any preventive guidance learned from the audit is written back into `.trellis/spec/`.
|
||||||
|
|
||||||
|
## Definition of Done (team quality bar)
|
||||||
|
|
||||||
|
- Audit completed across Android managers
|
||||||
|
- Code changes applied only where signature comparison justifies them
|
||||||
|
- Relevant docs/specs updated if a reusable lesson is found
|
||||||
|
- Build/test verification run as feasible for the touched layer
|
||||||
|
|
||||||
|
## Out of Scope (explicit)
|
||||||
|
|
||||||
|
- Changing SDK core public APIs
|
||||||
|
- Large redesign of the plugin dispatch/reflection mechanism unless separately required
|
||||||
|
- iOS behavior changes unless needed for parity documentation
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
- Touched/focus paths:
|
||||||
|
- `android/src/main/java/io/openim/flutter_openim_sdk/manager/*.java`
|
||||||
|
- `ios/Classes/Module/*.swift`
|
||||||
|
- `lib/src/manager/*.dart`
|
||||||
|
- `/mnt/d/workspace/go/im_dev/openim-sdk-core/open_im_sdk/*.go`
|
||||||
|
- Initial likely-risk buckets:
|
||||||
|
- Go `int` / `int64` parameters exposed to Android as Java `long`
|
||||||
|
- Pagination / cursor params inconsistently forwarded via `value(...)`
|
||||||
|
- Confirmed fixed bug:
|
||||||
|
- `ChannelManager.getChannelHistoryMessages`: `count` now uses `int2long(...)`
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"id": "audit-android-int-long-native-parameter-mismatches-against-sdk-core",
|
||||||
|
"name": "audit-android-int-long-native-parameter-mismatches-against-sdk-core",
|
||||||
|
"title": "audit android int-long native parameter mismatches against sdk core",
|
||||||
|
"description": "",
|
||||||
|
"status": "in_progress",
|
||||||
|
"dev_type": null,
|
||||||
|
"scope": null,
|
||||||
|
"package": null,
|
||||||
|
"priority": "P2",
|
||||||
|
"creator": "gem",
|
||||||
|
"assignee": "gem",
|
||||||
|
"createdAt": "2026-05-12",
|
||||||
|
"completedAt": null,
|
||||||
|
"branch": null,
|
||||||
|
"base_branch": "main",
|
||||||
|
"worktree_path": null,
|
||||||
|
"commit": null,
|
||||||
|
"pr_url": null,
|
||||||
|
"subtasks": [],
|
||||||
|
"children": [],
|
||||||
|
"parent": null,
|
||||||
|
"relatedFiles": [],
|
||||||
|
"notes": "",
|
||||||
|
"meta": {}
|
||||||
|
}
|
||||||
@@ -69,5 +69,5 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
//implementation 'com.openim:sdkcore:1.0.15-local'
|
//implementation 'com.openim:sdkcore:1.0.15-local'
|
||||||
implementation 'com.openim:sdkcore:1.0.22'
|
implementation 'com.openim:sdkcore:1.0.24'
|
||||||
}
|
}
|
||||||
@@ -100,4 +100,14 @@ public class ChannelManager extends BaseManager {
|
|||||||
jsonValue(methodCall, "userIDs")
|
jsonValue(methodCall, "userIDs")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getChannelHistoryMessages(MethodCall methodCall, MethodChannel.Result result) {
|
||||||
|
Open_im_sdk.getChannelHistoryMessages(
|
||||||
|
new OnBaseListener(result, methodCall),
|
||||||
|
value(methodCall, "operationID"),
|
||||||
|
value(methodCall, "channelID"),
|
||||||
|
int2long(methodCall, "count"),
|
||||||
|
int2long(methodCall, "sinceSeq")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ public class ChannelManager: BaseServiceManager {
|
|||||||
|
|
||||||
|
|
||||||
self["getUsersInChannel"] = getUsersInChannel
|
self["getUsersInChannel"] = getUsersInChannel
|
||||||
|
self["getChannelHistoryMessages"] = getChannelHistoryMessages
|
||||||
self["isJoinChannel"] = isJoinChannel
|
self["isJoinChannel"] = isJoinChannel
|
||||||
self["joinChannel"] = joinChannel
|
self["joinChannel"] = joinChannel
|
||||||
self["quitChannel"] = quitChannel
|
self["quitChannel"] = quitChannel
|
||||||
@@ -50,6 +51,10 @@ public class ChannelManager: BaseServiceManager {
|
|||||||
methodCall[jsonString: "userIDs"])
|
methodCall[jsonString: "userIDs"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getChannelHistoryMessages(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
|
Open_im_sdkGetChannelHistoryMessages(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "channelID"], methodCall[int: "count"], methodCall[int64: "sinceSeq"])
|
||||||
|
}
|
||||||
|
|
||||||
func isJoinChannel(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
func isJoinChannel(methodCall: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||||
Open_im_sdkIsJoinChannel(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "channelID"])
|
Open_im_sdkIsJoinChannel(BaseCallback(result: result), methodCall[string: "operationID"], methodCall[string: "channelID"])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'flutter_openim_sdk'
|
s.name = 'flutter_openim_sdk'
|
||||||
s.version = '0.0.16'
|
s.version = '0.0.18'
|
||||||
s.summary = 'A new Flutter project.'
|
s.summary = 'A new Flutter project.'
|
||||||
s.description = <<-DESC
|
s.description = <<-DESC
|
||||||
A new Flutter project.
|
A new Flutter project.
|
||||||
@@ -19,7 +19,7 @@ A new Flutter project.
|
|||||||
|
|
||||||
#s.ios.vendored_frameworks = 'frameworks/*.xcframework'
|
#s.ios.vendored_frameworks = 'frameworks/*.xcframework'
|
||||||
#s.vendored_frameworks = 'frameworks/*.xcframework'
|
#s.vendored_frameworks = 'frameworks/*.xcframework'
|
||||||
s.dependency 'openim_sdk_core_ios','0.16.0'
|
s.dependency 'openim_sdk_core_ios','0.18.0'
|
||||||
s.static_framework = true
|
s.static_framework = true
|
||||||
s.library = 'resolv'
|
s.library = 'resolv'
|
||||||
|
|
||||||
|
|||||||
@@ -309,6 +309,25 @@ class ChannelManager {
|
|||||||
'operationID': Utils.checkOperationID(operationID),
|
'operationID': Utils.checkOperationID(operationID),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
/// Get channel short-term history messages
|
||||||
|
/// [channelID] Channel ID
|
||||||
|
/// [count] Number of messages to retrieve
|
||||||
|
/// [sinceSeq] Internal sequence cursor for pagination (0 for first page)
|
||||||
|
Future<dynamic> getChannelHistoryMessages({
|
||||||
|
required String channelID,
|
||||||
|
int count = 20,
|
||||||
|
int sinceSeq = 0,
|
||||||
|
String? operationID,
|
||||||
|
}) =>
|
||||||
|
_channel.invokeMethod(
|
||||||
|
'getChannelHistoryMessages',
|
||||||
|
_buildParam({
|
||||||
|
'channelID': channelID,
|
||||||
|
'count': count,
|
||||||
|
'sinceSeq': sinceSeq,
|
||||||
|
'operationID': Utils.checkOperationID(operationID),
|
||||||
|
}));
|
||||||
|
|
||||||
static Map _buildParam(Map<String, dynamic> param) {
|
static Map _buildParam(Map<String, dynamic> param) {
|
||||||
param["ManagerName"] = "channelManager";
|
param["ManagerName"] = "channelManager";
|
||||||
param = Utils.cleanMap(param);
|
param = Utils.cleanMap(param);
|
||||||
|
|||||||
Reference in New Issue
Block a user