📜 ⬆️ ⬇️

XMPP-SMS gateway on Android



Introduction

The reason for writing this article was the need to create a program for the Android system, with which you can send order data in the form of SMS messages to the owners of online stores that an order of goods or services was ordered. Previously, I used a system including a GSM modem and a program written in C ++, using AT commands to communicate with the modem and the gloox library to receive messages using the XMPP protocol, on the side of the website used the xmpphp library to send order data. With such a scheme, it was necessary to keep the computer on all the time, since the order-taking system worked around the clock, respectively, hence the additional power consumption, the noise from the fans at night, and the constant monitoring of the Internet connection.

The main task of the program, which we will create throughout the article, is to receive a message of a certain format using the XMPP protocol and then transmit the received data via SMS. The development environment will be Eclipse with the ADT plugin installed and the required SDKs . SMACK for Android devices will be used for XMPP communication .

1. Sending SMS

First, we will create the framework of our application, which we will build up with the necessary functionality in the future. To do this, create in Eclipse, Android Project (Ctrl + N - Android - Android Project) with the following data:
')


After creating a new project, we will add the necessary permission (Permission) in the AndroidManifest.xml file for the ability to send SMS messages. To do this, in the Eclipse development environment, open the AndroidManifest.xml file, go to the Permissions tab, click the “Add ...” button, select the “Uses Permission” option in the window that appears, click the “OK” button, then choose the permission option, find and select from the list select the android.permission.SEND_SMS item, save our actions. After all the manipulations, the Permissions tab will look like this:



Now, for example, consider the easiest way to send an SMS message, which you can test in a regular Android emulator. To do this, create two new virtual devices using Android Virtual Device Manager (Window - AVD Manager) with the following parameters:



In the project we created, in the onCreate method , add the following code:

package ru.blagin.xmppsmsgate; import android.app.Activity; import android.os.Bundle; import android.telephony.SmsManager; public class XMPPSMSGateActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); SmsManager sms = SmsManager.getDefault(); sms.sendTextMessage("5556",null,"Text SMS",null,null); } } 


Next, we launch both Android emulators, each of which will have its own number to check sending and receiving SMS messages, when launching and initializing emulators is complete, you need to run our application on the emulator with the number 5554, after the application starts, the emulator with the number 5556 will receive our SMS message .



Sending SMS messages was carried out using the SmsManager class, which allows the Android system to perform the necessary actions with SMS messages. To initialize an object of this class, the static method SmsManager.getDefault () was used. Sending an SMS message is performed using the sendTextMessage method, where the method parameters are:

destinationAddress - the number to which the message is sent;
scAddress - The number of the SMS center of your cellular operator through which the message is sent, if this parameter has a zero value, then the default number is used;
text - SMS text message;
sentIntent - If not a null value, then a PendingIntent object is sent to this parameter to receive messages about the result of sending the message;
deliveryIntent - If not a null value, then a PendingIntent object is passed to this parameter to receive messages about the result of the message delivery.

The Android emulator does an excellent job with the tasks assigned to it, but it has several limitations, for example, it is not possible to check the result of message delivery on it, for this you have to use a real device, which will be done later. Also, when sending a message using the sendTextMessage method, its length cannot exceed 160 characters. For longer messages, you must use the sendMultipartTextMessage method, which in turn also allows you to send messages less than 160 characters long.
In the example above, we do not receive notifications about sending an SMS message and its delivery to the recipient, therefore, we will expand the functionality of the application by adding the necessary processing. To do this, you need to register two receivers of broadcast intentions in the application, which will process the necessary intentions and display the corresponding text messages on the screen.

In the resource editor, add a TextView widget to our main application window to display information on the screen.
 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:id="@+id/textView" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="top|left"/> </LinearLayout> 


Application code after making changes will take the following form:

 package ru.blagin.xmppsmsgate; import java.util.ArrayList; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.telephony.SmsManager; import android.widget.TextView; public class XMPPSMSGateActivity extends Activity { TextView tv = null; String SENT = "SMS_SENT"; String DELIVERED = "SMS_DELIVERED"; private BroadcastReceiver sent = null; private BroadcastReceiver delivered = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // TextView      tv = (TextView) findViewById(R.id.textView); //  :  IntentFilter in_sent = new IntentFilter(SENT); sent = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { tv.append(intent.getStringExtra("PARTS")+": "); tv.append(intent.getStringExtra("MSG")+": "); switch(getResultCode()) { case Activity.RESULT_OK: tv.append("SMS \n"); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: tv.append(" \n"); break; case SmsManager.RESULT_ERROR_NO_SERVICE: tv.append(" \n"); break; case SmsManager.RESULT_ERROR_NULL_PDU: tv.append("Null PDU\n"); break; case SmsManager.RESULT_ERROR_RADIO_OFF: tv.append(" \n"); break; } } }; registerReceiver(sent, in_sent); //  :  IntentFilter in_delivered = new IntentFilter(DELIVERED); delivered = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { tv.append(intent.getStringExtra("PARTS")+": "); tv.append(intent.getStringExtra("MSG")+": "); switch (getResultCode()) { case Activity.RESULT_OK: tv.append("SMS \n"); break; case Activity.RESULT_CANCELED: tv.append("SMS  \n"); break; } } }; registerReceiver(delivered, in_delivered); SendSMS("_","  > 160 ."); } //  SMS  public void SendSMS(String phone, String message) { SmsManager sms = SmsManager.getDefault(); ArrayList<String> al_message = new ArrayList<String>(); al_message = sms.divideMessage(message); ArrayList<PendingIntent> al_piSent = new ArrayList<PendingIntent>(); ArrayList<PendingIntent> al_piDelivered = new ArrayList<PendingIntent>(); for (int i = 0; i < al_message.size(); i++) { Intent sentIntent = new Intent(SENT); sentIntent.putExtra("PARTS", ": "+i); sentIntent.putExtra("MSG", ": "+al_message.get(i)); PendingIntent pi_sent = PendingIntent.getBroadcast(this, i, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT); al_piSent.add(pi_sent); Intent deliveredIntent = new Intent(DELIVERED); deliveredIntent.putExtra("PARTS", ": "+i); deliveredIntent.putExtra("MSG", ": "+al_message.get(i)); PendingIntent pi_delivered = PendingIntent.getBroadcast(this, i, deliveredIntent, PendingIntent.FLAG_UPDATE_CURRENT); al_piDelivered.add(pi_delivered); } sms.sendMultipartTextMessage(phone, null, al_message, al_piSent, al_piDelivered); } @Override protected void onDestroy() { if(sent != null) unregisterReceiver(sent); if(delivered != null) unregisterReceiver(delivered); super.onDestroy(); } } 


To test the sending of messages and receive a delivery receipt, on a real device, you can use your own cellular number, then you can fully test the application. The figure below shows sending and receiving a long message.



2. Creating a service to work on the XMPP protocol

Now we need to add the ability to communicate over our protocol XMPP to our application. For these purposes we will create a service (Service) that will work in the background. The service, using the SMACK library, will receive and process messages. Then, using broadcast intentions, the data from the received message will be transmitted to the main class of the application, for display on the screen and then sent via SMS.

In order to add a service to our application, you need to define an implementation class for it, to do this, in the Eclipse development environment, right-click the project package name, select New and Class in the menu that appears, as shown in the figure:



In the window that appears, fill in the necessary items indicated on the image and click the “Finish” button:



After these actions, the implementation of the class appears in the project, then you need to register the service class in the AndroidManifest.xml file, to do this, open the file in the Eclipse development environment, go to the Application tab, and in the Application Nodes section, click the Add button, in the window that appears, select the Service item and press the "OK" button. After you need to specify the name of the service class, after all the manipulations the Application tab will look like this:



Now we will add another permission for the application so that it can access the Internet, for this, follow the same steps as above to allow the sending of an SMS message, only this time select android.permission.INTERNET .

The next step is to add the SMACK library to the application, download it at http://code.google.com/p/asmack/, save it in the project folder, then open the project properties, right-click on the project name in the Eclipse development environment in the menu that appears, select Properties. In the project settings window that appears, select the Java Build Path item in the left list, then click the “Add External JARs ...” button, find the previously saved library in the project folder and add it. After adding an external library, the settings window will look as follows:



Below is the source code of the service, as seen in the onCreate method, a separate stream is created in which the main work on XMPP communication takes place using the SMACK library. Received messages, as well as other state of the service are transmitted by sending broadcast intentions.

 package ru.blagin.xmppsmsgate; import java.util.Collection; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ChatManager; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.PacketListener; import org.jivesoftware.smack.SASLAuthentication; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; import org.jivesoftware.smack.packet.Presence; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class XMPPSMSGateService extends Service { private ConnectionConfiguration connConfig; private XMPPConnection connection; Thread th = null; Intent in = new Intent("SMSGate_Service"); @Override public IBinder onBind(Intent arg0){return null;} @Override public int onStartCommand(Intent intent, int flags, int startId){return Service.START_STICKY;} @Override public void onCreate() { super.onCreate(); th= new Thread() { public void run() { in.putExtra("Message","The service is started"); sendBroadcast(in); connConfig = new ConnectionConfiguration(/*domen*/,5222,/*server*/); SASLAuthentication.supportSASLMechanism("PLAIN"); connConfig.setCompressionEnabled(false); connConfig.setSASLAuthenticationEnabled(true); connection = new XMPPConnection(connConfig); try { in.putExtra("Message","Connect to the XMPP server"); sendBroadcast(in); connection.connect(); in.putExtra("Message","Login into the XMPP server"); sendBroadcast(in); connection.login(/*login*/,/*password*/); if(connection.isConnected()) { in.putExtra("Message","SMS Gate online."); sendBroadcast(in); }else { in.putExtra("Message","SMS Gate offline."); sendBroadcast(in); } Presence presence = new Presence(Presence.Type.available); presence.setStatus("SMS Gate"); presence.setPriority(30); connection.sendPacket(presence); PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class)); PacketListener myListener = new PacketListener() { public void processPacket(Packet packet) { if(packet instanceof Message) { Message message = (Message) packet; String messageBody = message.getBody(); String JID = message.getFrom(); if(messageBody == null) { messageBody = ""; Collection<Message.Body> bodies = message.getBodies(); for(Message.Body r:bodies){messageBody += r.getMessage();} } if(messageBody.equals("ping")){sendMessage(JID,"pong");} in.putExtra("Message",messageBody); sendBroadcast(in); } } }; connection.addPacketListener(myListener, filter); while(connection.isConnected()) { try{Thread.sleep(1000);}catch(Exception e){Log.e(this.getClass().getName(),e.getMessage());} } }catch(Exception e) { Log.e(this.getClass().getName(),e.getMessage()); in.putExtra("Message","ERROR: "+e.getMessage()); sendBroadcast(in); } } public void sendMessage(String to, String message) { if(!message.equals("")) { ChatManager chatmanager = connection.getChatManager(); Chat newChat = chatmanager.createChat(to, null); try{newChat.sendMessage(message);} catch(Exception e) {Log.e(this.getClass().getName(),e.getMessage());} } } }; th.start(); } @Override public void onDestroy() { if(connection.isConnected()){connection.disconnect();th = null;} in.putExtra("Message","The service is stopped"); sendBroadcast(in); } } 


To process broadcast intentions received from the service, you need to register another receiver of broadcast intentions. To do this, add the following code to the onCreate method of the main application class:

 IntentFilter filter = new IntentFilter(); filter.addAction("SMSGate_Service"); service = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if(intent.getAction().equals("SMSGate_Service")) { String message = intent.getStringExtra("Message"); tv.append(message+"\n"); int i = message.indexOf("@"); if(i != -1) { String phone = message.substring(0, i); if(phone.length() != 0) { String text = message.substring(i+1,message.length()); tv.append("Sending SMS...\n"); SendSMS(phone,text); }else{/*phone: 0*/} }else{/*not sms message*/} } } }; registerReceiver(service, filter); 


As can be seen from the above code, for sending SMS, messages that are of the type number telephone_message_ message are processed, for example:

5556@

To start the service, in the main application class, add the line to the onCreate method:
 startService(new Intent(this,XMPPSMSGateService.class)); 

To stop the service, in the main application class in the onDestroy method, add the line:
 stopService(new Intent(this,XMPPSMSGateService.class)); 

Now we will try to start the application in the emulator and send a message of a certain type through any IM client. The result is shown in the image:



Conclusion

This article is an introductory and designed for novice programmers, whom I myself am. The created application has several disadvantages and limitations, for example, sending an SMS message will be carried out only when the main application window is active. There is no check for Internet access, as well if Internet access is only via WI-FI, then when the device goes into sleep mode, WI-FI is turned off, to save battery power. This problem can be avoided by using the Wi-Fi Keep Alive application or by adding this functionality to the application yourself. There is no logging of received and sent messages.

The scope of application of this application is quite wide, from receiving notifications about the order to creating a full-fledged SMS gateway for processing various data.

The source code of the application.

Bibliography
  1. Alexey Goloshchapov, Google Android. Programming for mobile devices, 2012
  2. Alexey Goloshchapov, Google Android. System components and network communications, 2012
  3. Java XMPP bot using Smack API ( esin ).

Source: https://habr.com/ru/post/135627/


All Articles