Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Src/Authorizer.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
using System.Timers;

namespace EmotivUnityPlugin
Expand All @@ -17,6 +18,8 @@ public class Authorizer
private string _licenseID = "";
private static int _debitNo = 5000; // default value
private static double _currentLoginTime = 0; // store current login time
private TaskCompletionSource<AIDataConsent> _getAiDataConsentTcs; // pending GetAIDataConsent request
private TaskCompletionSource<AIDataConsent> _setAiDataConsentTcs; // pending SetAIDataConsent request

/// <summary>
/// Timer for waiting a user login
Expand Down Expand Up @@ -77,6 +80,8 @@ public Authorizer()
_ctxClient.EULANotAccepted += OnEULANotAccepted;
_ctxClient.RefreshTokenOK += OnRefreshTokenOK;
_ctxClient.GetLicenseInfoDone += OnGetLicenseInfoDone;
_ctxClient.GetAiDataConsentDone += OnGetAiDataConsentDone;
_ctxClient.SetAiDataConsentDone += OnSetAiDataConsentDone;
_ctxClient.ErrorMsgReceived += OnErrorMsgReceived;
}

Expand All @@ -101,6 +106,18 @@ private void OnErrorMsgReceived(object sender, ErrorMsgEventArgs errorInfo)

UnityEngine.Debug.Log($"OnErrorMsgReceived: Code={errorInfo.Code}, Message={errorInfo.MessageError}, Method={errorInfo.MethodName}");

// fault pending AI data consent requests so callers awaiting them do not hang
if (errorInfo.MethodName == "getAiDataConsent")
{
_getAiDataConsentTcs?.TrySetException(new Exception(errorInfo.MessageError));
_getAiDataConsentTcs = null;
}
else if (errorInfo.MethodName == "setAiDataConsent")
{
_setAiDataConsentTcs?.TrySetException(new Exception(errorInfo.MessageError));
_setAiDataConsentTcs = null;
}

#if UNITY_ANDROID || UNITY_IOS || USE_EMBEDDED_LIB
// For mobile and embedded lib platforms
bool shouldLogout = false;
Expand Down Expand Up @@ -204,6 +221,48 @@ private void OnGetLicenseInfoDone(object sender, License lic)
}
}

/// <summary>
/// Request AI data usage consent information for the current logged-in user directly from Cortex, and waits for the response.
/// </summary>
public async Task<AIDataConsent> GetAIDataConsent() {
string cortexToken = CortexToken;
if (String.IsNullOrEmpty(cortexToken))
return null;
// avoid overwriting a pending request's TCS, which would leave its caller awaiting forever
if (_getAiDataConsentTcs != null && !_getAiDataConsentTcs.Task.IsCompleted)
throw new InvalidOperationException("A GetAIDataConsent request is already in progress.");
_getAiDataConsentTcs = new TaskCompletionSource<AIDataConsent>(TaskCreationOptions.RunContinuationsAsynchronously);
_ctxClient.GetAiDataConsent(cortexToken);
return await _getAiDataConsentTcs.Task;
}

private void OnGetAiDataConsentDone(object sender, AIDataConsent consent)
{
UnityEngine.Debug.Log("OnGetAiDataConsentDone: " + consent.Accepted);
_getAiDataConsentTcs?.TrySetResult(consent);
}

/// <summary>
/// Set user's consent to the use of their data for AI training purposes, and waits for the response from Cortex.
/// </summary>
public async Task<AIDataConsent> SetAIDataConsent(bool accepted) {
string cortexToken = CortexToken;
if (String.IsNullOrEmpty(cortexToken))
return null;
// avoid overwriting a pending request's TCS, which would leave its caller awaiting forever
if (_setAiDataConsentTcs != null && !_setAiDataConsentTcs.Task.IsCompleted)
throw new InvalidOperationException("A SetAIDataConsent request is already in progress.");
_setAiDataConsentTcs = new TaskCompletionSource<AIDataConsent>(TaskCreationOptions.RunContinuationsAsynchronously);
_ctxClient.SetAiDataConsent(cortexToken, accepted);
return await _setAiDataConsentTcs.Task;
}

private void OnSetAiDataConsentDone(object sender, AIDataConsent consent)
{
UnityEngine.Debug.Log("OnSetAiDataConsentDone: " + consent.Accepted);
_setAiDataConsentTcs?.TrySetResult(consent);
}

private void OnRefreshTokenOK(object sender, string cortexToken)
{
UnityEngine.Debug.Log("The cortex token is refreshed successfully.");
Expand Down
17 changes: 17 additions & 0 deletions Src/BCIGameItf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,23 @@ public string GetCurrentEmotivId()
return emotivUnityItf.GetCurrentEmotivId();
}

/// <summary>
/// Requests the AI data usage consent directly from Cortex and waits for the response.
/// </summary>
public async Task<AIDataConsent> GetAIDataConsent()
{
return await emotivUnityItf.GetAIDataConsent();
}

/// <summary>
/// Sets user's consent to the use of their data for AI training purposes, and waits for the response.
/// </summary>
/// <param name="accepted">True to accept, false to decline.</param>
public async Task<AIDataConsent> SetAIDataConsent(bool accepted)
{
return await emotivUnityItf.SetAIDataConsent(accepted);
}

/// <summary>
/// Get the mental command action sensitivity for the first trained action except neutral (pull action).
/// Should be called after training is completed. Default sensitivity is 5
Expand Down
27 changes: 23 additions & 4 deletions Src/CortexClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ public abstract class CortexClient
public event EventHandler<bool> AccessRightGrantedDone;
public event EventHandler<string> AuthorizeOK;
public event EventHandler<UserDataInfo> GetUserLoginDone;
public event EventHandler<AIDataConsent> GetAiDataConsentDone;
public event EventHandler<string> EULAAccepted;
public event EventHandler<string> EULANotAccepted; // return cortexToken if user has not accept eula to proceed next step
public event EventHandler<AIDataConsent> SetAiDataConsentDone;
public event EventHandler<string> UserLoginNotify;
public event EventHandler<string> UserLogoutNotify;
public event EventHandler<License> GetLicenseInfoDone;
Expand Down Expand Up @@ -331,9 +333,10 @@ private void HandleResponse(string method, JToken data)
License lic = new License(data["license"]);
GetLicenseInfoDone(this, lic);
}
else if (method == "getUserInformation")
else if (method == "getAiDataConsent")
{
//TODO
AIDataConsent consent = new AIDataConsent(data["aiDataConsent"]);
GetAiDataConsentDone?.Invoke(this, consent);
}
Comment thread
tungntEmotiv marked this conversation as resolved.
else if (method == "authorize")
{
Expand All @@ -357,6 +360,11 @@ private void HandleResponse(string method, JToken data)
string message = data["message"].ToString();
EULAAccepted(this, message);
}
else if (method == "setAiDataConsent")
{
AIDataConsent consent = new AIDataConsent(data["aiDataConsent"]);
SetAiDataConsentDone?.Invoke(this, consent);
}
else if (method == "createSession")
{
string sessionId = (string)data["id"];
Expand Down Expand Up @@ -674,12 +682,23 @@ public void GetLicenseInfo(string cortexToken)
);
SendTextMessage(param, "getLicenseInfo", true);
}
public void GetUserInformation(string cortexToken)
// get user's consent to the use of their data for AI training purposes
public void GetAiDataConsent(string cortexToken)
{
JObject param = new JObject(
new JProperty("cortexToken", cortexToken)
);
SendTextMessage(param, "getUserInformation", true);
SendTextMessage(param, "getAiDataConsent", true);
}

// set user's consent to the use of their data for AI training purposes
public void SetAiDataConsent(string cortexToken, bool accepted)
{
JObject param = new JObject(
new JProperty("cortexToken", cortexToken),
new JProperty("accepted", accepted)
);
SendTextMessage(param, "setAiDataConsent", true);
}

// Login
Expand Down
16 changes: 16 additions & 0 deletions Src/EmotivUnityItf.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ public string GetCurrentEmotivId()
return _authorizer.CurrentEmotivId;
}

/// <summary>
/// Requests the AI data usage consent directly from Cortex and waits for the response.
/// </summary>
public async Task<AIDataConsent> GetAIDataConsent()
{
return await _authorizer.GetAIDataConsent();
}

/// <summary>
/// Sets user's consent to the use of their data for AI training purposes, and waits for the response.
/// </summary>
public async Task<AIDataConsent> SetAIDataConsent(bool accepted)
{
return await _authorizer.SetAIDataConsent(accepted);
}


#if USE_EMBEDDED_LIB || UNITY_ANDROID || UNITY_IOS
private CrossPlatformBrowser _crossPlatformBrowser;
Expand Down
13 changes: 13 additions & 0 deletions Src/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,19 @@ public License(JToken licObj) {
public int totalDebit = 0;
}

public class AIDataConsent
{
public AIDataConsent(JToken consentObj) {
// null means AI Data Consent has not been accepted/declined for the latest policy version
Accepted = consentObj["accepted"] != null && consentObj["accepted"].Type != JTokenType.Null
? (bool?)consentObj["accepted"]
: null;
Url = consentObj["licenseUrl"] != null ? consentObj["licenseUrl"].ToString() : "";
}
public bool? Accepted { get; private set; }
public string Url { get; private set; }
}

// contain data and time of data. For example, login time and user login or token and time for token
[Serializable()]
public class UserDataInfo : ISerializable
Expand Down