[AI Task] [Tizen.Network.WiFi] Consolidate GetFound*/GetWiFiConfigurations into EnumerateForeach<T> helper#7680
[AI Task] [Tizen.Network.WiFi] Consolidate GetFound*/GetWiFiConfigurations into EnumerateForeach<T> helper#7680JoonghyunCho wants to merge 2 commits into
Conversation
… EnumerateForeach<T> helper GetFoundAPs, GetFoundSpecificAPs, GetFoundBssids and GetWiFiConfigurations each repeated the same 'native foreach callback -> List collection' pattern, differing only by the native foreach function, the handle clone call, and the wrapper type. Introduces a generic EnumerateForeach<T> helper that takes the operation name, privilege, native foreach function, and an item-wrap delegate. The wrap delegate absorbs the differing Clone signatures (AP.Clone has the out handle first; Config.Clone has it second). Iteration semantics, ret-check timing, exception mapping (CheckReturnValue) and per-method privilege are preserved, so behavior is unchanged. Fixes #7622
There was a problem hiding this comment.
Code Review
This pull request refactors the GetFoundAPs, GetFoundSpecificAPs, GetFoundBssids, and GetWiFiConfigurations methods by introducing a shared helper method, EnumerateForeach<T>, to eliminate code duplication when iterating over native handles. The feedback recommends catching and safely rethrowing exceptions within the native callback to prevent undefined behavior from propagating managed exceptions across P/Invoke boundaries. Additionally, the reviewer advises checking the return values of the native clone operations (Interop.WiFi.AP.Clone and Interop.WiFi.Config.Clone) to handle potential cloning failures gracefully.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| private List<T> EnumerateForeach<T>(string opName, string privilege, | ||
| Func<SafeWiFiManagerHandle, Interop.WiFi.HandleCallback, IntPtr, int> nativeForeach, | ||
| Func<IntPtr, T> wrap) | ||
| { | ||
| Log.Info(Globals.LogTag, "GetFoundAPs"); | ||
| List<WiFiAP> apList = new List<WiFiAP>(); | ||
| Interop.WiFi.HandleCallback callback = (IntPtr apHandle, IntPtr userData) => | ||
| Log.Info(Globals.LogTag, opName); | ||
| List<T> list = new List<T>(); | ||
| Interop.WiFi.HandleCallback callback = (IntPtr handle, IntPtr userData) => | ||
| { | ||
| if (apHandle != IntPtr.Zero) | ||
| if (handle != IntPtr.Zero) | ||
| { | ||
| IntPtr clonedHandle; | ||
| Interop.WiFi.AP.Clone(out clonedHandle, apHandle); | ||
| WiFiAP apItem = new WiFiAP(clonedHandle); | ||
| apList.Add(apItem); | ||
| list.Add(wrap(handle)); | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
|
|
||
| int ret = Interop.WiFi.GetForeachFoundAPs(GetSafeHandle(), callback, IntPtr.Zero); | ||
| CheckReturnValue(ret, "GetForeachFoundAPs", PrivilegeNetworkGet); | ||
|
|
||
| return apList; | ||
| int ret = nativeForeach(GetSafeHandle(), callback, IntPtr.Zero); | ||
| CheckReturnValue(ret, opName, privilege); | ||
| return list; | ||
| } |
There was a problem hiding this comment.
Propagating managed exceptions through native code boundaries (P/Invoke) can lead to undefined behavior, native crashes, or silent failures. Since the wrap delegate can throw exceptions (e.g., if cloning fails or during object construction), it is highly recommended to catch any exceptions inside the callback, stop the iteration, and safely rethrow the captured exception after the native foreach method returns.
private List<T> EnumerateForeach<T>(string opName, string privilege,
Func<SafeWiFiManagerHandle, Interop.WiFi.HandleCallback, IntPtr, int> nativeForeach,
Func<IntPtr, T> wrap)
{
Log.Info(Globals.LogTag, opName);
List<T> list = new List<T>();
Exception capturedException = null;
Interop.WiFi.HandleCallback callback = (IntPtr handle, IntPtr userData) =>
{
if (handle != IntPtr.Zero)
{
try
{
list.Add(wrap(handle));
}
catch (Exception ex)
{
capturedException = ex;
return false;
}
return true;
}
return false;
};
int ret = nativeForeach(GetSafeHandle(), callback, IntPtr.Zero);
if (capturedException != null)
{
throw capturedException;
}
CheckReturnValue(ret, opName, privilege);
return list;
}| apHandle => | ||
| { | ||
| IntPtr clonedHandle; | ||
| Interop.WiFi.AP.Clone(out clonedHandle, apHandle); | ||
| WiFiAP apItem = new WiFiAP(clonedHandle); | ||
| apList.Add(apItem); | ||
| return true; | ||
| } | ||
| return false; | ||
|
|
||
| }; | ||
|
|
||
| int ret = Interop.WiFi.GetForeachFoundSpecificAPs(GetSafeHandle(), callback, IntPtr.Zero); | ||
| CheckReturnValue(ret, "GetForeachFoundSpecificAPs", PrivilegeNetworkGet); | ||
|
|
||
| return apList; | ||
| } | ||
|
|
||
| internal IEnumerable<WiFiAP> GetFoundBssids() | ||
| { | ||
| Log.Info(Globals.LogTag, "GetFoundBssids"); | ||
| List<WiFiAP> apList = new List<WiFiAP>(); | ||
| Interop.WiFi.HandleCallback callback = (IntPtr apHandle, IntPtr userData) => | ||
| { | ||
| if (apHandle != IntPtr.Zero) | ||
| Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle); | ||
| return new WiFiAP(clonedHandle); | ||
| }); |
There was a problem hiding this comment.
The return value of Interop.WiFi.AP.Clone is ignored. If cloning fails, it may return a non-zero error code and leave clonedHandle as IntPtr.Zero or invalid, which will cause downstream failures when the WiFiAP instance is used. It is safer to check the return value and throw an exception if cloning fails.
apHandle =>
{
int ret = Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle);
if (ret != (int)WiFiError.None)
{
WiFiErrorFactory.ThrowWiFiException(ret, "Failed to clone AP handle");
}
return new WiFiAP(clonedHandle);
});| apHandle => | ||
| { | ||
| IntPtr clonedHandle; | ||
| Interop.WiFi.AP.Clone(out clonedHandle, apHandle); | ||
| WiFiAP apItem = new WiFiAP(clonedHandle); | ||
| apList.Add(apItem); | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
|
|
||
| int ret = Interop.WiFi.GetForeachFoundBssids(GetSafeHandle(), callback, IntPtr.Zero); | ||
| CheckReturnValue(ret, "GetForeachFoundBssids", PrivilegeNetworkGet); | ||
|
|
||
| return apList; | ||
| } | ||
|
|
||
| internal IEnumerable<WiFiConfiguration> GetWiFiConfigurations() | ||
| { | ||
| Log.Debug(Globals.LogTag, "GetWiFiConfigurations"); | ||
| List<WiFiConfiguration> configList = new List<WiFiConfiguration>(); | ||
| Interop.WiFi.HandleCallback callback = (IntPtr configHandle, IntPtr userData) => | ||
| { | ||
| if (configHandle != IntPtr.Zero) | ||
| Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle); | ||
| return new WiFiAP(clonedHandle); | ||
| }); |
There was a problem hiding this comment.
The return value of Interop.WiFi.AP.Clone is ignored. If cloning fails, it may return a non-zero error code and leave clonedHandle as IntPtr.Zero or invalid, which will cause downstream failures when the WiFiAP instance is used. It is safer to check the return value and throw an exception if cloning fails.
apHandle =>
{
int ret = Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle);
if (ret != (int)WiFiError.None)
{
WiFiErrorFactory.ThrowWiFiException(ret, "Failed to clone AP handle");
}
return new WiFiAP(clonedHandle);
});| apHandle => | ||
| { | ||
| IntPtr clonedConfig; | ||
| Interop.WiFi.Config.Clone(configHandle, out clonedConfig); | ||
| WiFiConfiguration configItem = new WiFiConfiguration(clonedConfig); | ||
| configList.Add(configItem); | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
|
|
||
| int ret = Interop.WiFi.Config.GetForeachConfiguration(GetSafeHandle(), callback, IntPtr.Zero); | ||
| CheckReturnValue(ret, "GetForeachConfiguration", PrivilegeNetworkProfile); | ||
| return configList; | ||
| } | ||
| Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle); | ||
| return new WiFiAP(clonedHandle); | ||
| }); |
There was a problem hiding this comment.
The return value of Interop.WiFi.AP.Clone is ignored. If cloning fails, it may return a non-zero error code and leave clonedHandle as IntPtr.Zero or invalid, which will cause downstream failures when the WiFiAP instance is used. It is safer to check the return value and throw an exception if cloning fails.
apHandle =>
{
int ret = Interop.WiFi.AP.Clone(out IntPtr clonedHandle, apHandle);
if (ret != (int)WiFiError.None)
{
WiFiErrorFactory.ThrowWiFiException(ret, "Failed to clone AP handle");
}
return new WiFiAP(clonedHandle);
});| configHandle => | ||
| { | ||
| Interop.WiFi.Config.Clone(configHandle, out IntPtr clonedConfig); | ||
| return new WiFiConfiguration(clonedConfig); | ||
| }); |
There was a problem hiding this comment.
The return value of Interop.WiFi.Config.Clone is ignored. If cloning fails, it may return a non-zero error code and leave clonedConfig as IntPtr.Zero or invalid, which will cause downstream failures when the WiFiConfiguration instance is used. It is safer to check the return value and throw an exception if cloning fails.
configHandle =>
{
int ret = Interop.WiFi.Config.Clone(configHandle, out IntPtr clonedConfig);
if (ret != (int)WiFiError.None)
{
WiFiErrorFactory.ThrowWiFiException(ret, "Failed to clone WiFi configuration");
}
return new WiFiConfiguration(clonedConfig);
});|
🤖 [AI Review] Reviewed — no findings. Scope checked:
No 🔴 critical issues, no 🟡 suggestions to flag. Automated review — final merge decision rests with human reviewers. |
Harden EnumerateForeach<T>: catch exceptions raised inside the native foreach callback so they never unwind across the P/Invoke boundary, and rethrow after the native call returns. Check the return value of every AP.Clone / Config.Clone call and throw a WiFiException when cloning fails. Applied-Human-Comments: 3369222130,3369222132,3369222133,3369222134,3369222136
|
🤖 [AI Review] |
Summary
Consolidates the four near-identical "native foreach callback →
Listcollection" methods inWiFiManagerImplinto a single genericEnumerateForeach<T>helper.Changes
src/Tizen.Network.WiFi/Tizen.Network.WiFi/WiFiManagerImpl.csEnumerateForeach<T>(opName, privilege, nativeForeach, wrap)helper.GetFoundAPs,GetFoundSpecificAPs,GetFoundBssids,GetWiFiConfigurations.Behavior preservation
truefor a non-zero handle, and returnsfalseotherwise.CheckReturnValueis still called immediately after the native foreach returns, with the same operation name and the same per-method privilege (PrivilegeNetworkGetfor the AP/Bssid getters,PrivilegeNetworkProfilefor configurations).wrapdelegate absorbs the differing clone signatures:Interop.WiFi.AP.Clone(out cloned, original)vsInterop.WiFi.Config.Clone(origin, out cloned).internal).Mode
Refactoring (behavior-preserving)
Verification
dotnet build src/Tizen.Network.WiFi/Tizen.Network.WiFi.csproj -c Release, 0 errors / 0 warnings)sdbdevice connected —sdb devicesreports no targets; manual benchmark verification required)Fixes #7622