Android Tutorial Intent Filters - Intercepting SMS messages in the background

Status
Not open for further replies.
Old tutorial. Don't use services. Use receivers.
Broadcast receivers are program components that can handle broadcasted messages. These messages usually notify about a system event.

There are two types of receivers in Android: statically registered receivers and dynamically registered receivers.

Static registered receivers are receivers that are declared in the manifest file.
Dynamic registered receivers are registered at runtime by calling the Java registerReceiver method.

In Basic4android you can register dynamic receivers with the BroadcastReceiver library. PhoneEvents and SmsInterceptor objects from the Phone library also use dynamic registration to listen for common intents.

Difference between static and dynamic receivers
The main difference between the two types of receivers is that dynamic receivers listen to intents as long as the process is running.

Static receivers always work. If the process is not running then it will be created.

Normal processes eventually get killed. This means that you cannot rely on dynamic receivers to intercept intents when your application is in the background. A possible workaround is to call Service.StartForeground in the service. This will prevent the process from being killed. However this will also add an ongoing notification icon (see the Services tutorial for more information).

So if you need to always listen for a specific type of intents then you may prefer to use a static receiver. Note that some intents can only be intercepted with dynamic receivers.

Static receivers
Each service module in Basic4android is made of two components. The service and a receiver. The receiver responsibility is to delegate broadcast intents to the service. For example when you call StartServiceAt, it is the receiver that actually intercepts the intent and wakes the service.

If we want to listen for intents we need to define an intent filter in the manifest file. See this link for more information about intent filters.

For example if we want to listen for intents with the action: android.provider.Telephony.SMS_RECEIVED we will need to add the following manifest editor code:
B4X:
AddPermission(android.permission.RECEIVE_SMS)
AddReceiverText(s1,
<intent-filter>
    <action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>)
s1 is the name of the service module that the intent will be delegated to.
We also add the RECEIVE_SMS permission which is required in this case.

Once this program is installed the service will be started each time that an Sms message arrives. It doesn't matter whether the process is running or not.

In our Service_Start event we check the intent action. If it fits then the messages will be parsed from the intent. This is done with the help of the Reflection library:
B4X:
'Service module
Sub Process_Globals
   Type Message (Address As String, Body As String)
End Sub
Sub Service_Create

End Sub

Sub Service_Start(startingIntent As Intent)
   If startingIntent.Action = "android.provider.Telephony.SMS_RECEIVED" Then
      Dim messages() As Message
      messages = ParseSmsIntent(startingIntent)
      For i = 0 To messages.Length - 1
         Log(messages(i))
      Next
   End If
   Service.StopAutomaticForeground
End Sub

'Parses an SMS intent and returns an array of messages
Sub ParseSmsIntent (in As Intent) As Message()
   Dim messages() As Message
   If in.HasExtra("pdus") = False Then Return messages
   Dim pdus() As Object
   Dim r As Reflector
   pdus = in.GetExtra("pdus")
   If pdus.Length > 0 Then
      Dim messages(pdus.Length) As Message
      For i = 0 To pdus.Length - 1
         r.Target = r.RunStaticMethod("android.telephony.SmsMessage", "createFromPdu", _
            Array As Object(pdus(i)), Array As String("[B"))
         messages(i).Body = r.RunMethod("getMessageBody")
         messages(i).Address = r.RunMethod("getOriginatingAddress")
      Next
   End If
   Return messages
End Sub

Update 2018: Static intent filters will cause the service to start while the app is not in the foreground. This means that on newer devices it will only work with B4A v8+ and that we need to call Service.StopAutomaticForeground once the task has completed.
https://www.b4x.com/android/forum/threads/automatic-foreground-mode.90546/#post-572424
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Would this foreground state not be lost once another Activity is opened(come to the foreground) since they are on the same thread?
No.

When you call Service.Startforeground, Android treats the process as if it is showing an Activity. The process is expected to keep running. On extreme low memory conditions Android may kill it.

You can use the start sticky solution together with Service.Starteforeground. Then even in the rare case where the process is killed, Android will recreate the process and service.
 

gawie007

Member
Licensed User
Longtime User
No.

When you call Service.Startforeground, Android treats the process as if it is showing an Activity. The process is expected to keep running. On extreme low memory conditions Android may kill it.

You can use the start sticky solution together with Service.Starteforeground. Then even in the rare case where the process is killed, Android will recreate the process and service.
Hi Erel,
That addresses my concerns!
Thank You for your time, patience and sharing your great knowledge!
 

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
Erel,
I think I know how to intercept the voice-trigger of google glass, can you give a little help?
First a little explanation how works this "voice-trigger" in google glass:
When you start the google glass, a little screen with the words "OK, GLASS" appear and in this moment
you can say "OK, GLASS", after that you can say anything to others APPs, like the defaults: "GOOGLE", etc...
Then, I the GDK (Google Glass SDK) have a minimal example to intercept a frase, like "start voice demo" and the
code is all in manifest and resources, look this

AndroidManifest.xml
=======================================================

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.google.android.glass.action.VOICE_TRIGGER" />
</intent-filter>
<meta-data
android:name="com.google.android.glass.VoiceTrigger"
android:resource="@xml/voiceinput_voicedemo" />


voiceinput_voicedemo.xml
=======================================================
<?xml version="1.0" encoding="utf-8"?>
<trigger keyword="@string/voicedemo_voice_trigger">
<input prompt="@string/voicedemo_voice_prompt" />
<constraints microphone="true" />
</trigger>


strings.xml
=======================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="voicedemo_voice_trigger">start voice demo</string>

<string name="voicedemo_voice_prompt">next action</string>
</resources>


I think to intercept a little frase and then do anything else is put this lines in AndroidManifest.xml, look if I´m wrong:

AddReceiverText(ListenTrigger,<intent-filter><action android:name="com.google.android.glass.action.VOICE_TRIGGER" /></intent-filter>)
AddActivityText(Main,<meta-data android:name="com.google.android.glass.VoiceTrigger" android:value="start voice demo"/>)


The <meta-data> in java sample is correct in B4A? In Java uses resource, but in B4A I put value directly, is correct to do that?

Thanks!

PS: If this works, I put the code in here to help to others users....

PS2: Look this another sample to say: "say hello"

https://github.com/luisdelarosa/HelloGlass/commit/c5038ed2ff019306becb32211354358833b6fafc

PS3: This sample is good too: https://github.com/jzplusplus/GlassRoll/blob/master/AndroidManifest.xml
 
Last edited:

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
Finally!!

Erel, look in my Sample. It´s works now.. I must to create manually the files to intercept in this intent VOICE_TRIGGER

1. XML/voice_trigger_start.xml with <trigger keyword="@string/start_stopwatch" />
2. VALUES/strings.xml with my name to identify "Albert" <string name="start_stopwatch">Albert</string>

I must to put READ ONLY in those files to protect with delete by B4A

And with B4A in Manifest Editor, I put:

AddReceiverText(ListenTrigger,<intent-filter><action android:name="com.google.android.glass.action.VOICE_TRIGGER" /></intent-filter>)

And look the results:

When You start Glass:
OkGlass.jpg


After you Say "OK GLASS"
MenuAfterOK.jpg


or after you touch the Glass
OptionAfterTouch.jpg


And After I say "Albert", I put a simple message in TOASTMESSAGE to test
AfterSayAlbert.jpg
 

Attachments

  • GoogleGlassVoiceTrigger.zip
    7.8 KB · Views: 800

Pablo Torres

Active Member
Licensed User
Longtime User
I'm Definitively doing something wrong ... but I don't know what I'm doing wrong
Can anyone help me?

B4X:
'Service module

#Region  Service Attributes
    #StartCommandReturnValue: android.app.Service.START_STICKY
    #StartAtBoot: True
#End Region

Sub Process_Globals
    Type Message (Address As String, Body As String)
    Dim PrintBuffer As String
    Dim cmp20 As Serial
    Dim printer As TextWriter
End Sub

Sub Service_Create
Dim SI As SmsInterceptor
  SI.Initialize2("SI", 999)
  cmp20.Initialize("Printer")
End Sub

Sub Service_Start(startingIntent As Intent)
  If startingIntent.Action = "android.provider.Telephony.SMS_RECEIVED" Then
      Dim messages() As Message
      messages = ParseSmsIntent(startingIntent)
      For i = 0 To messages.Length - 1
        Log(messages(i))
      Next
  End If
End Sub

'Parses an SMS intent and returns an array of messages
Sub ParseSmsIntent (In As Intent) As Message()
  Dim messages() As Message
  If In.HasExtra("pdus") = False Then Return messages
  Dim pdus() As Object
  Dim r As Reflector
  pdus = In.GetExtra("pdus")
  If pdus.Length > 0 Then
      Dim messages(pdus.Length) As Message
      For i = 0 To pdus.Length - 1
        r.Target = r.RunStaticMethod("android.telephony.SmsMessage", "createFromPdu", _
            Array As Object(pdus(i)), Array As String("[B"))
        messages(i).Body = r.RunMethod("getMessageBody")
        messages(i).Address = r.RunMethod("getOriginatingAddress")
      Next
  End If
  Return messages
End Sub

Sub Service_Destroy

End Sub

Sub ImprimirSms(Body As String, Address As String)

      PrintBuffer= " " & Chr(13)& Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " Numero:" & Address & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " Mensaje:" & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Body & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
    StartPrinter

End Sub


Sub StartPrinter
    Dim PairedDevices As Map

    PairedDevices.Initialize

    Try
        PairedDevices = cmp20.GetPairedDevices
    Catch
        Msgbox("Imposible conectar Bluetooth","Error de Impresión")
        printer.Close
        cmp20.Disconnect
    End Try

    If PairedDevices.Size = 0 Then
        Return
    Else
        cmp20.Connect("98:D3:31:B2:1D:FF")
    End If
   
End Sub

Sub Printer_Connected (Success As Boolean)
    If Success Then
        printer.Initialize(cmp20.OutputStream)
        printer.Write(PrintBuffer)
        printer.Flush
        printer.Close
        cmp20.Disconnect
    Else
        StartPrinter
    End If
End Sub

Sub SI_MessageReceived (From As String, Body As String) As Boolean
  ImprimirSms(Body,From)
  Return True
End Sub

B4X:
'Main module

#Region  Project Attributes
    #ApplicationLabel: M
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)
    StartService(s1)
    Activity.Finish
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
 

barx

Well-Known Member
Licensed User
Longtime User
I'm Definitively doing something wrong ... but I don't know what I'm doing wrong
Can anyone help me?

B4X:
'Service module

#Region  Service Attributes
    #StartCommandReturnValue: android.app.Service.START_STICKY
    #StartAtBoot: True
#End Region

Sub Process_Globals
    Type Message (Address As String, Body As String)
    Dim PrintBuffer As String
    Dim cmp20 As Serial
    Dim printer As TextWriter
End Sub

Sub Service_Create
Dim SI As SmsInterceptor
  SI.Initialize2("SI", 999)
  cmp20.Initialize("Printer")
End Sub

Sub Service_Start(startingIntent As Intent)
  If startingIntent.Action = "android.provider.Telephony.SMS_RECEIVED" Then
      Dim messages() As Message
      messages = ParseSmsIntent(startingIntent)
      For i = 0 To messages.Length - 1
        Log(messages(i))
      Next
  End If
End Sub

'Parses an SMS intent and returns an array of messages
Sub ParseSmsIntent (In As Intent) As Message()
  Dim messages() As Message
  If In.HasExtra("pdus") = False Then Return messages
  Dim pdus() As Object
  Dim r As Reflector
  pdus = In.GetExtra("pdus")
  If pdus.Length > 0 Then
      Dim messages(pdus.Length) As Message
      For i = 0 To pdus.Length - 1
        r.Target = r.RunStaticMethod("android.telephony.SmsMessage", "createFromPdu", _
            Array As Object(pdus(i)), Array As String("[B"))
        messages(i).Body = r.RunMethod("getMessageBody")
        messages(i).Address = r.RunMethod("getOriginatingAddress")
      Next
  End If
  Return messages
End Sub

Sub Service_Destroy

End Sub

Sub ImprimirSms(Body As String, Address As String)

      PrintBuffer= " " & Chr(13)& Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " Numero:" & Address & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " Mensaje:" & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Body & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
      PrintBuffer=PrintBuffer & " " & Chr(13)&Chr(10)
    StartPrinter

End Sub


Sub StartPrinter
    Dim PairedDevices As Map

    PairedDevices.Initialize

    Try
        PairedDevices = cmp20.GetPairedDevices
    Catch
        Msgbox("Imposible conectar Bluetooth","Error de Impresión")
        printer.Close
        cmp20.Disconnect
    End Try

    If PairedDevices.Size = 0 Then
        Return
    Else
        cmp20.Connect("98:D3:31:B2:1D:FF")
    End If
  
End Sub

Sub Printer_Connected (Success As Boolean)
    If Success Then
        printer.Initialize(cmp20.OutputStream)
        printer.Write(PrintBuffer)
        printer.Flush
        printer.Close
        cmp20.Disconnect
    Else
        StartPrinter
    End If
End Sub

Sub SI_MessageReceived (From As String, Body As String) As Boolean
  ImprimirSms(Body,From)
  Return True
End Sub

B4X:
'Main module

#Region  Project Attributes
    #ApplicationLabel: M
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.

End Sub

Sub Activity_Create(FirstTime As Boolean)
    StartService(s1)
    Activity.Finish
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub


Without even looking at your code, what makes you think something is wrong?? do you get errors? if so what? Does is not work? if not, what version of android? etc etc
 

Pablo Torres

Active Member
Licensed User
Longtime User
Without even looking at your code, what makes you think something is wrong?? do you get errors? if so what? Does is not work? if not, what version of android? etc etc
Android 4.1
It compile
I don't get any errors
I even can install it
It doesnt do anything
It doesnt print anything
Can you please take a look at the code this time, probably there is something very stupid that I'm missing
Thanks
 

Pablo Torres

Active Member
Licensed User
Longtime User
Perhaps I don't explain well my situation:
I DON'T UNDERSTAND HOW TO USE A SERVICE
I DON'T KNOW HOW TO CATCH THE MESSAGE
Is not obvious?
Can anybody take a look at THE CODE and tell me what is wrong IN THE CODE?
I DONT WANT ANSWERS LIKE "YOU SHOULD READ THIS..." OR "TRY THIS..." cause I already read and already tried and CANT GET IT TO WORK.
THANKS
 

BarryW

Active Member
Licensed User
Longtime User
Hi Masters, can you post a running program that intercept the text message. I cant run ur example.
 

Troberg

Well-Known Member
Licensed User
Longtime User
Once this program is installed the service will be started each time that an Sms message arrives.

When does it shut down? Is it done automatically at some point, or does it simply fall into the Android "let's kill it eventually when we need the resources"-limbo? Should one exit it explicitly through code?
 

lemonisdead

Well-Known Member
Licensed User
Longtime User
When does it shut down?
The service is started and you have to decide when you want it to shutdown. If required, you have to explicitly exit the service by code. Or of course, the system can kill it if it is not a Sticky service.
 

pesquera

Active Member
Licensed User
Longtime User
Hi Erel, I'm using this technique succesfully to intercept sms since 2 years ago.. but, recently I've detected that some sms are truncated and can not figure out what is hapening.. I did some changes that ar not complex at all
I've resumed the code to focus on the problem.. TestVar has a length of 150 characters on the wrong cases
thanks for any help
Checking on the sms inbox (handcent app), I can see that the sms received has more characters than TestVar
B4X:
Sub GLO_SmsInterceptor_MessageReceived (From As String, Body As String) As Boolean
...
        GLO_SMS = Body
..
dim TestVar as String = GLO_SMS.SubString(3)
 

giacomo-italy

Member
Licensed User
Longtime User
Hello Erel,
I used your example code to listen to sms with a static receiver. Everything works fine.
I used your code to write an app that manages my home alarm system, and notifications of my PV system (both work via sms). Great.
When my PV system sends the production data at the end of the day, with a sms, the app intercepts in the background, decodes the data and writes it on an Excel spreadsheet and a database. Very helpful. The same for the management and notification of my alarm system.

But I want to know what code i need to write in Service_Destroy if I wanted to stop the service by calling StopService () (if it is possible to stop the service...)
Thanks a lot.
 
Status
Not open for further replies.
Top