Binary SMS in Symbian - binary

I wonder if anyone has managed to create a working code for sending out binary messages (to configure Symbian phones) and have also some binary data sample.
So far all the samples I have found fail to leave the Outbox or never return.
// Current entry is the Draft folder.
iSmsMtm->SwitchCurrentEntryL( KMsvDraftEntryId );
// Create a new SMS message entry as a child of the current context.
iSmsMtm->CreateMessageL( KUidMsgTypeSMS.iUid );
CMsvEntry& serverEntry = iSmsMtm->Entry();
TMsvEntry entry( serverEntry.Entry() );
/* Send Binary SMS */
CSmsHeader &hdr = iSmsMtm->SmsHeader();
CSmsMessage &msg = hdr.Message();
CSmsPDU &pdu = msg.SmsPDU();
CSmsUserData &userdata = pdu.UserData();
// Set the DCS byte
pdu.SetBits7To4(TSmsDataCodingScheme::ESmsDCSTextUncompressedWithNoClassInfo);
pdu.SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet8Bit);
pdu.SetClass(ETrue, TSmsDataCodingScheme::ESmsClass2);
char buf[]= {...}; //my binary data, 247 bytes long
// Construct a dummy message
HBufC8 * iMessage = HBufC8::NewL(300);
TPtr8 TempUDHBufDesc((TUint8*)buf,247,247);
iMessage->Des().Copy(TempUDHBufDesc);
_LOGFENTRY1(_L("mess length %d"),iMessage->Des().Length());
userdata.SetBodyL(*iMessage);
delete iMessage;
// Message will be sent immediately.
entry.SetSendingState( KMsvSendStateWaiting );
entry.iDate.UniversalTime(); // insert current time //Solution for HomeTime()
// Set the SMS message settings for the message.
CSmsHeader& header = iSmsMtm->SmsHeader();
CSmsSettings* settings = CSmsSettings::NewL();
CleanupStack::PushL( settings );
settings->CopyL( iSmsMtm->ServiceSettings() ); // restore settings
settings->SetDelivery( ESmsDeliveryImmediately ); // to be delivered immediately
settings->SetDeliveryReport(EFalse);
settings->SetCharacterSet(TSmsDataCodingScheme::ESmsAlphabet8Bit); // IMPORTANT! For sending binary SMS
header.SetSmsSettingsL( *settings ); // new settings
// Let's check if there is a service center address.
if ( header.Message().ServiceCenterAddress().Length() == 0 )
{
// No, there isn't. We assume there is at least one service center
// number set and use the default service center number.
CSmsSettings* serviceSettings = &( iSmsMtm->ServiceSettings() );
// Check if number of service center addresses in the list is null.
if ( !serviceSettings->ServiceCenterCount() )
{ _LOGENTRY("No SC");
return ; // quit creating the message
}
else
{
CSmsNumber* smsCenter= CSmsNumber::NewL();
CleanupStack::PushL(smsCenter);
smsCenter->SetAddressL((serviceSettings->GetServiceCenter( serviceSettings->DefaultServiceCenter())).Address());
header.Message().SetServiceCenterAddressL( smsCenter->Address() );
CleanupStack::PopAndDestroy(smsCenter);
}
}
CleanupStack::PopAndDestroy( settings );
// Recipient number is displayed also as the recipient alias.
entry.iDetails.Set( _L("+3725038xxx") );
iSmsMtm->AddAddresseeL( _L("+3725038xxx") , entry.iDetails );
// Validate message.
if ( !ValidateL() )
{ _LOGENTRY("Not valid");
return ;
}
entry.SetVisible( ETrue ); // set message as visible
entry.SetInPreparation( EFalse ); // set together with the visibility flag
serverEntry.ChangeL( entry ); // commit changes
iSmsMtm->SaveMessageL(); // save message
TMsvSelectionOrdering selection;
CMsvEntry* parentEntry = CMsvEntry::NewL( iSmsMtm->Session(), KMsvDraftEntryId, selection );
CleanupStack::PushL( parentEntry );
// Move message to Outbox.
iOperation =parentEntry->MoveL( entry.Id(), KMsvGlobalOutBoxIndexEntryId, iStatus );
CleanupStack::PopAndDestroy( parentEntry );
iState = EWaitingForMoving;
SetActive();
Mostly I'm not sure about the correct values for port and class . Also some correct binary string would be nice to have for testing. Now I'm not sure if thecode is bad or the data.

Use the JSR120 specification and the wireless toolkit. they contain java example code that will work for sure.
These are implemented directly using RSocket objects in Symbian C++.
If you really want to do it in C++, the simplest way is to copy your TMsvEntry to the entry of the sms service. In your code above, that means using "iSmsMtm->ServiceId()" instead of "KMsvGlobalOutBoxIndexEntryId". also, just copy the message to the service but do move it to the outbox after it has been successfully sent.
shameless plug : http://www.quickrecipesonsymbianos.com will contain an explanation of the Symbian C++ messaging API will simple and reusable example code.

The solution that worked is to use RComm and "DATAPORT::1" to send out the binary SMS using AT commands (like using a modem).

If you want to send the SMS silently (and avoid the complexity of using the messaging APIs), you should send it via an RSocket: http://wiki.forum.nokia.com/index.php/How_to_send_an_SMS_using_sockets
Depending on your needs this might be more suitable than using the messaging APIs.

Related

Equivalent of Platform::IBoxArray in C++/WinRT

I am currently porting an UWP application from C++/CX to C++/WinRT. I encountered a safe_cast<Platform::IBoxArray<byte>^>(data) where data is of type Windows::Foundation::IInspectable ^.
I know that the safe_cast is represented by the as<T> method, and I know there are functions for boxing (winrt::box_value) and unboxing (winrt::unbox_value) in WinRT/C++.
However, I need to know the equivalent of Platform::IBoxArray in order to perform the cast (QueryInterface). According to https://learn.microsoft.com/de-de/cpp/cppcx/platform-iboxarray-interface?view=vs-2017, IBoxArray is the C++/CX equivalent of Windows::Foundation::IReferenceArray, but there is no winrt::Windows::Foundation::IReferenceArray...
Update for nackground: What I am trying to achieve is retrieving the view transform attached by the HoloLens to every Media Foundation sample from its camera. My code is based on https://github.com/Microsoft/HoloLensForCV, and I got really everything working except for this last step. The problem is located around this piece of code:
static const GUID MF_EXTENSION_VIEW_TRANSFORM = {
0x4e251fa4, 0x830f, 0x4770, 0x85, 0x9a, 0x4b, 0x8d, 0x99, 0xaa, 0x80, 0x9b
};
// ...
// In the event handler, which receives const winrt::Windows::Media::Capture::Frames::MediaFrameReader& sender:
auto frame = sender.TryAcquireLatestFrame();
// ...
if (frame.Properties().HasKey(MF_EXTENSION_VIEW_TRANSFORM)) {
auto /* IInspectable */ userData = frame.Properties().Lookup(MF_EXTENSION_VIEW_TRANSFORM);
// Now I would have to do the following:
// auto userBytes = safe_cast<Platform::IBoxArray<Byte> ^>(userData)->Value;
//viewTransform = *reinterpret_cast<float4x4 *>(userBytes.Data);
}
I'm also working on porting some code from HoloLensForCV to C++/WinRT. I came up with the following solution for a very similar case (but not the exact same line of code you ask about):
auto user_data = source.Info().Properties().Lookup(c_MF_MT_USER_DATA); // type documented as 'array of bytes'
auto source_name = user_data.as<Windows::Foundation::IReferenceArray<std::uint8_t>>(); // Trial and error to get the right specialization of IReferenceArray
winrt::com_array<std::uint8_t> arr;
source_name.GetUInt8Array(arr);
winrt::hstring source_name_str{ reinterpret_cast<wchar_t*>(arr.data()) };
Specifically, you can replace the safe_cast with .as<Windows::Foundation::IReferenceArray<std::uint8_t> for a boxed array of bytes. Then, I suspect doing the same cast as me (except to float4x4* instead of wchar_t*) will work for you.
The /ZW flag is not required for my example above.
I can't believe that actually worked, but using information from https://learn.microsoft.com/de-de/windows/uwp/cpp-and-winrt-apis/interop-winrt-cx, I came up with the following solution:
Enable "Consume Windows Runtime Extension" via /ZW and use the following conversion:
auto abi = reinterpret_cast<Platform::Object ^>(winrt::get_abi(userData));
auto userBytes = safe_cast<Platform::IBoxArray<byte> ^>(abi)->Value;
viewTransform = *reinterpret_cast<float4x4 *>(userBytes->Data);
Unfortunately, the solution has the drawback of generating
warning C4447: 'main' signature found without threading model. Consider using 'int main(Platform::Array^ args)'.
But for now, I can live with it ...

aws-sdk-cpp: how to use CurlHttpClient?

I need to make signed requests to AWS ES, but am stuck at the first hurdle in that I cannot seem to be able to use CurlHttpClient. Here is my code (verb, path, and body defined elsewhere):
Aws::Client::ClientConfiguration clientConfiguration;
clientConfiguration.scheme = Aws::Http::Scheme::HTTPS;
clientConfiguration.region = Aws::Region::US_EAST_1;
auto client = Aws::MakeShared<Aws::Http::CurlHttpClient>(ALLOCATION_TAG, clientConfiguration);
Aws::Http::URI uri;
uri.SetScheme(Aws::Http::Scheme::HTTPS);
uri.SetAuthority(ELASTIC_SEARCH_DOMAIN);
uri.SetPath(path);
Aws::Http::Standard::StandardHttpRequest req(uri, verb);
req.AddContentBody(body);
auto res = client->MakeRequest(req);
Aws::Http::HttpResponseCode resCode = res->GetResponseCode();
if (resCode == Aws::Http::HttpResponseCode::OK) {
Aws::IOStream &body = res->GetResponseBody();
rejoiceAndBeMerry();
}
else {
gotoPanicStations();
}
When executed, the code throws a bad_function_call deep from within the sdk mixed up with a lot of shared_ptr this and allocate that. My guess is that I am just using the SDK wrong, but I've been unable to find any examples that use the CurlHttpClient directly such as I need to do here.
How can I use CurlHttpClient?
You shouldn't be using the HTTP client directly, but the supplied wrappers with the aws-cpp-sdk-es package. Like previous answer(s), I would recommend evaluating the test cases shipped with the library to see how the original authors intended to implement the API (at least until the documents catch-up).
How can I use CurlHttpClient?
Your on the right track with managed shared resources and helper functions. Just need to create a static factory/client to reference. Here's a generic example.
using namespace Aws::Client;
using namespace Aws::Http;
static std::shared_ptr<HttpClientFactory> MyClientFactory; // My not be needed
static std::shared_ptr<HttpClient> MyHttpClient;
// ... jump ahead to method body ...
ClientConfiguration clientConfiguration;
MyHttpClient = CreateHttpClient(clientConfiguration);
Aws::String uri("https://example.org");
std::shared_ptr<HttpRequest> req(
CreateHttpRequest(uri,
verb, // i.e. HttpMethod::HTTP_POST
Utils::Stream::DefaultResponseStreamFactoryMethod));
req.AddContentBody(body); //<= remember `body' should be `std::shared_ptr<Aws::IOStream>',
// and can be created with `Aws::MakeShared<Aws::StringStream>("")';
req.SetContentLength(body_size);
req.SetContentType(body_content_type);
std::shared_ptr<HttpResponse> res = MyHttpClient->MakeRequest(*req);
HttpResponseCode resCode = res->GetResponseCode();
if (resCode == HttpResponseCode::OK) {
Aws::StringStream resBody;
resBody << res->GetResponseBody().rdbuf();
rejoiceAndBeMerry();
} else {
gotoPanicStations();
}
I encountered exactly the same error when trying to download from S3 using CurlHttpClient.
I fixed it by instead modelling my code after the integration test found in the cpp sdk:
aws-sdk-cpp/aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp
Search for the test called TestObjectOperationsWithPresignedUrls.

The existing data required by the data writer could not be found

On my page for an add-on i am creating works perfectly well unless i try to update an existing row. `public function actionUpdate()
{
$visitor = XenForo_Visitor::getInstance();
$userName = $visitor['username'];
//Get the text that user wrote in the text box
$text3 = $this->_input->filterSingle('simple_text2', XenForo_Input::STRING);
$text4 = $this->_input->filterSingle('simple_text3', XenForo_Input::STRING);
//Create a instance of our DataWriter
$dwSimpleText = XenForo_DataWriter::create('MinecraftAdder_DataWriter_MinecraftAdder');
//Set the field with the data we filtered
$dwSimpleText->setExistingData('Name');
$dwSimpleText->set('Name', $text3);
$dwSimpleText->setExistingData('Rank');
$dwSimpleText->set('Rank', XenForo_Visitor::getUserId());
$dwSimpleText->setExistingData('UUID');
$dwSimpleText->set('UUID', $text4);
$dwSimpleText->setExistingData('UserID');
$dwSimpleText->set('UserID', $userName);
//Save in the database, please!
$dwSimpleText->save();
//Send a response to the user, so he know that everything went fine with this action
return $this->responseRedirect(
XenForo_ControllerResponse_Redirect::SUCCESS,
$this->getDynamicRedirect()
);
}`
I get the error The existing data required by the data writer could not be found. Does anyone know how to fix this?
My Addon page
Your usage of setExistingData is incorrect. That function must be given one of these two values:
The value of the Primary Key column(s) for the row you want to update
An array with all the data for the row you want to update
So in your case, seeing as you don't select the row in advance, you'd use option 1. Assuming UserID is your Primary Key column, your code would be:
public function actionUpdate() {
$visitor = XenForo_Visitor::getInstance();
$userName = $visitor['username'];
//Get the text that user wrote in the text box
$text3 = $this->_input->filterSingle('simple_text2', XenForo_Input::STRING);
$text4 = $this->_input->filterSingle('simple_text3', XenForo_Input::STRING);
//Create a instance of our DataWriter
$dwSimpleText = XenForo_DataWriter::create('MinecraftAdder_DataWriter_MinecraftAdder');
//Set the field with the data we filtered
$dwSimpleText->setExistingData($userName);
$dwSimpleText->set('Name', $text3);
$dwSimpleText->set('Rank', XenForo_Visitor::getUserId());
$dwSimpleText->set('UUID', $text4);
//Save in the database, please!
$dwSimpleText->save();
//Send a response to the user, so he know that everything went fine with this action
return $this->responseRedirect(
XenForo_ControllerResponse_Redirect::SUCCESS,
$this->getDynamicRedirect()
);
}
Some other tips:
If this actionUpdate may be accessed through a form only, you should add $this->_assertPostOnly(); on the top of the function to make sure the request was a POST request.
You may want to check if the Visitor's user_id is 0 so you don't save the information of guests. (Unless that's what you want of course)

AS3 How to Send Multiple Messages in writeUTF?

So with TCP in AS3, I'm trying to write strings over to the server and then to the clients, but it appears to only be able to read one at a time. This is not good because I'm sending messages based on keys being pressed, and the client has an action that needs to be taken place based on what key is pressed. Since multiple keys can be pressed at a time, obviously it does not work correctly.
Client example:
if (keys[p1_jump_key])
{
p1_up = "down";
sock.writeUTF("p1_up_down");
sock.flush();
}
else
{
p1_up = "up";
sock.writeUTF("p1_up_up");
sock.flush();
}
if (keys[p1_crouch_key])
{
p1_down = "down";
sock.writeUTF("p1_down_down");
sock.flush();
}
else
{
p1_down = "up";
sock.writeUTF("p1_down_up");
sock.flush();
}
And then here is the server:
function socketDataHandler(event:ProgressEvent):void{
var socket:Socket = event.target as Socket;
var message:String = socket.readUTF();
for each (var socket:Socket in clientSockets)
{
socket.writeUTF(message);
socket.flush();
}}
And finally, here is the recieving client (I have a method that allows the server to differentiate between the two):
if(msg=="p1_down_down"){
p1_down="down";
}
if(msg=="p1_down_up"){
p1_down="up";
}
if(msg=="p1_up_down"){
p1_down="down";
}
if(msg=="p1_up_up"){
p1_down="up";
}
Now many of you already see the issue, as when the down key is up, it sends the message "p1_down_up". When the up key is up, it sends the message "p1_up_up". Both messages are sending at once when neither of them are being pressed. The receiving client is, I suppose, just getting one of the signals, or perhaps neither of them. How do I make MULTIPLE signals get wrote and read over the server? I tried using an array but you can't write those apparently. Thank you.
For anyone else who comes across this like I did trying to figure out how to accomplish this, I have to say that the other solution supplied is highly inefficient, overly complicated and too hard to read. Instead of adding a null byte to the end of your message and pushing packets into arrays, you can simply have the server send the length of the entire message to determine whether the message is in multiple packets, and to make sure you read multiple writes in the correct order.
Here is a working client receive function. I send data as compressed Objects converted to ByteArrays. Objects can store any type of data which make them a good candidate for transferring complex information from server to client.
var messageBytes: ByteArray = new ByteArray;
var messageLength: int = 0;
var readBuffer = false;
function receiveTCP(e: ProgressEvent): void {
while (e.target.bytesAvailable >= 4) {//Loop to see if there is data in the socket that is available to read.
if (messageLength == 0) {
messageLength = e.target.readUnsignedInt();//read the first 4 bytes of data to get the size of the message and store it as our message length.
readBuffer = true;//set the readBuffer to true. While this is true, the function wont be reading any more data from the server until the current message is finished being read.
}
if (messageLength <= e.target.bytesAvailable) {//check to see if our message is finished being read.
if (readBuffer == true) {//Make sure another message isn't being read. Without this check, another incoming message from the server will overwrite the current one from being executed.
e.target.readBytes(messageBytes, 0, messageLength);//reads the byte array into our messageBytes ByteArray declared at the top.
messageBytes.uncompress();//decompresses our message
var message: Object = new Object();
message = messageBytes.readObject();//Writes our ByteArray into an object again.
receiveMessage(message);//send our message to another function for the data to be executed.
messageLength = 0;//reset our messageLength for the next message.
readBuffer = false;//reset our buffer so that the client can read the next message being received, or the next message in line.
} else {
receiveTCP(e);//if there is another message in wait and the previous one isn't finished reading, execute the same function with the same event data again. Eventually the buffer will open and the message will be read.
}
} else {
break;//If the message is split up into multiple packets, this will reset the loop to continue reading the message until all the packets are accounted for.
}
}
}
This will prevent Clogging, allow you to receive multiple messages simultaneously and put together messages that were delivered in multiple packets.
Admittedly, this took a while to figure out. If you need the serverside code for sending the message, let me know.
The TCP protocol consists of a bi-directional stream of bytes, and as such message boundaries are not preserved. Due to this, if you are sending messages too often, you can end up reading from the buffer two messages instead of one.
Thus, some of the messages you are sending might be being processed together, which fails the comparison test on the receiving end.
To fix this, you can append a delimiter at the end of each message. This way, you'll know where every message you sent begins and ends, allowing you to split and process them separately. This also means you don't need to flush after every write to the buffer.
For instance, you could use a null byte (\0) as delimiter.
if(keys[p1_jump_key])
{
p1_up = "down";
sock.writeUTF("p1_up_down\0");
}
...
sock.flush();
And on the receiving end, we trim any trailing delimiters and then split the message into the smaller packets.
EDIT: Fixing the issue you described gives rise to another one: your messages might be being split over two buffer reads. I've edited my code to reflect a solution to this, by creating a temporary variable where partial messages are stored, to be prepended to the other part(s) of the message in the following reads.
// First we check to see if there are any 'parts' of a message that we've stored temporarily from the last read. If so, prepend them to the message string, which must be its continuation
if(temp != "")
{
msg = temp + msg;
temp = "";
}
// Now we begin processing our packet
var splitPackets:Array = msg.split("\0");
var packets:Array = new Array();
// Deal with incomplete message reads
if(splitPackets[splitPackets.length - 1] != "")
{
// If the last item in our array isn't empty, it means out message stream ended in a partial message. Hence, we have to store this value temporarily, and append it on the next read of the stream
temp = splitPackets[splitPackets.length - 1];
}
// If the last item in our array is empty, it means our message stream ends in a full message
for(var i = 0; i < splitPackets.length - 1; i++)
{
packets[i] = splitPackets[i];
}
for(var i = 0; i < packets.length; i++)
{
if(packets[i] == "p1_down_down")
{
// Process each packet individually here
...
// If you have already processed the packet, move on to the next
continue;
}
// Processing code for other packets
...
}
Hope this helps!

AS3 / AIR readObject() from socket - How do you check all data has been received?

If you write a simple object to a socket:
var o:Object = new Object();
o.type = e.type;
o.params = e.params;
_socket.writeObject(o);
_socket.flush();
Then on the client do you simply use:
private function onData(e:ProgressEvent):void
{
var o:Object = _clientSocket.readObject();
}
Or do you have to implement some way of checking all of the data has been received recieved before calling .readObject()
There's 2 approaches:
If you're confident that your object will fit into one packet, you can do something like:
var fromServer:ByteArray = new ByteArray;
while( socket.bytesAvailable )
socket.readBytes( fromServer );
fromServer.position = 0;
var myObj:* = fromServer.readObject();
If you have the possibility of having multiple packet messages, then a common usage is to prepend the message with the length of the message. Something like (pseudo code):
var fromServer:ByteArray = new ByteArray();
var msgLen:int = 0;
while ( socket.bytesAvailable > 0 )
{
// if we don't have a message length, read it from the stream
if ( msgLen == 0 )
msgLen = socket.readInt();
// if our message is too big for one push
var toRead:int = ( msgLen > socket.bytesAvailable ) ? socket.bytesAvailable : msgLen;
msgLen -= toRead; // msgLen will now be 0 if it's a full message
// read the number of bytes that we want.
// fromServer.length will be 0 if it's a new message, or if we're adding more
// to a previous message, it'll be appended to the end
socket.readBytes( fromServer, fromServer.length, toRead );
// if we still have some message to come, just break
if ( msgLen != 0 )
break;
// it's a full message, create your object, then clear fromServer
}
Having your socket able to read like this will mean that multiple packet messages will be read properly as well as the fact that you won't miss any messages where 2 small messages are sent almost simultaneously (as the first message will treat it all as one message, thereby missing the second one)
Rule # 1 when dealing with TCP: it is an octet stream transfer protocol. You may never ever assume anything about how many octets (8 bit long values, commonly called bytes) you get in one go, always write code that can deal with any amount, both too few and too many. There is no gurantee that the write will not be split into multiple reads. There is also no gurantee that a single read will be from a single write.
The way I handled it was to make a call back that the server tell the client that the null bit was received.
The null bit is appended to the end of the data string you are sending to the server.
String.fromCharCode(0)
Also in your case you are doing
_socket.writeObject(o);
You should be sending a string not an object.
So Like this.
_socket.writeUTFBytes( 'Hellow World" + String.fromCharCode(0) );
NOTE *************
And one thing that most first time socket creators over look is the
fact that the first request to from the client to the server over the
port that the socket is connected on is a request for the
crossdomainpolicy.xml
If you only wish to send Objects, the simplest solution is if you send an int(size) before every object. Its not important to send the exact size, you can send a bit less. In my case, I've sent a bitmapdata, and the width and height of the object. obviously the bitmapdata's size is so big, its okay if you send only that, and ignore the rest.
var toRead=0;
protected function onSocketData(event:ProgressEvent):void{
if(toRead==0) {
toRead=socket.readInt()
}
if(socket.bytesAvailable>toRead){
var recieved:Object=socket.readObject()
/*do stuff with recieved object*/
toRead=0
}