Program: JCBloc
An Android application (app) to block telemarketing (junk) calls.
GENERAL DESCRIPTION:
This program listens for an incoming call and captures the call's number.
It then compares that number to numbers stored in a blacklist. If a match
is found, it blocks the call after one (short) ring. If the user has
voicemail turned on, the caller is directed to leave a message there.
LIMITATIONS:
The app runs on a smartphone running Android version 2.2 (API level 8).
It uses method startForeground(), which was introduced in version 2.0.
There is code available in the Android documentation that would allow it
to work with earlier versions (search for: startForeground).
The app will not run on smartphones running version 2.3 or newer, due to
a decision by the folks at Android to block permission MODIFY_PHONE_STATE
in those versions. This caused many apps written earlier to break. To
view the issues, google: Android MODIFY_PHONE_STATE.
This app uses classes and methods in Android package ITelephony
(import com.android.internal.telephony.ITelephony), which is a hidden
API. It provides program control and monitoring methods for telephone
operations. To bypass this limitation a procedure described by inazaruk
at his web site devmaze:
(http://devmaze.wordpress.com/
2011/01/18/using-com-android-internal-part-2-hacking-around/)
was used.
The procedure results in an android.jar file in which hidden (@hide) APIs
are made visible. The procedure is complex, but well documented. It is not
necessary to actually perform it since he supplies converted versions at
the end of Part 5. As indicated, the resulting android.jar may be used in
the Eclipse integrated development environment (IDE) for compiling an app.
Once compiled the app will run when loaded into an actual smartphone or
when testing using the Eclipse smartphone emulator, since the API is not
hidden in the android.jar files located there.
This program was developed using the Eclipse version 3.5.2 IDE and the
associated Android Compatibility Package, Java version 1.6.0_20 and the
OpenJDK Runtime Environment (IcedTea6 1.9.10). It was tested on an LG P509
OPTIMUS T TITANIUM smartphone. It may or may not operate correctly when
run on other Android smartphone brands. Instructions for downloading
these (free) packages may be found at the following sites:
Eclipse IDE for Java EE Developers:
http://www.eclipse.org/downloads/
Android Software Development Kit (SDK):
http://developer.android.com/sdk/index.html
Android Plug-in for Eclipse:
http://developer.android.com/sdk/eclipse-adt.html
Specific installation instructions are presented in Appendix A of:
"Sams Teach Yourself Android Application Development" sited in the
references below.
TRY IT:
A compiled version of the app, JCBloc.apk, is available at this site and
may be downloaded and run either on a smartphone running Android 2.2 or on
the Android 2.2 emulator provided in the Android Compatibility Package. An
easy way to install it is to use the adb (Android Debug Bridge) utility
program located at:
<install dir>/android/android-sdk-linux_x86/platform-tools/adb
To install on a smartphone, plug the phone into a USB port on your computer
and turn the smartphone on. Then change to the directory where JCBloc.apk
is located and run:
adb install ./JCBloc.apk
This should result in the display of some download data followed by:
SUCCESS. You should then be able to run the app from the smartphone's main
menu.
To run the program in the emulator, start Eclipse and then, from the menu
bar select:
Window | AVD Manager | FixedLevel8 | Start...
The level 8 (Android 2.2) emulator should start (it takes some time). If
you enter:
adb devices
you should see the the ID of the emulator (probably emulator-5554). You
can then install the app with:
adb -s emulator-5554 install ./JCBloc.apk
or just:
adb install ./JCBloc.apk
if this is the only target (i.e., the smartphone has been removed). You can
display all adb options by running:
adb -h
SOURCE CODE:
File JCBloc_v0.1_EclipseWorkspaceDir.tar.gz may be downloaded from this
site. It contains a compressed image of the JCBloc directory generated by
the Eclipse IDE in the user's workspace directory. The directory was packed
into a tar (Tape ARchive) file using the Linux tar utility. It was then
compressed using the Linux gzip utility. It may be uncompressed and unpacked
on Linux and Windows platforms as follows (similar tools are available for
Apple, etc. machines):
On Lunux:
tar xzvfp JCBloc_v0.1_EclipseworkspaceDir.tar.gz
or you can unzip it first with:
gunzip JCBloc_v0.1_EclipseworkspaceDir.tar.gz
and then run:
tar xvfp JCBloc_v0.1_EclipseworkspaceDir.tar
On Windows:
There are several options here. There's an open source program 7-Zip.
You could also download program TarTool. I will describe the 7-Zip
method. It can be run as a command:
7z x JCBloc_v0.1_EclipseworkspaceDir.tar.gz
Alternatively, a graphical interface may be started from:
Start | All Programs | 7-Zip | 7-Zip File Manager
Change to the directory where you downloaded the target file and select
it. Then click the Extract button.
All of these techniques create directory JCBloc_v0.1_EclipseWorkspaceDir.
In this directory is directory JCBloc. This is the directory that
Eclipse creates in the user's workspace. The Java source files are in
subdirectory src/jcbloc.project (or src\jcbloc\project on Windows). The
various other files needed to build the program (e.g., various xml files)
are also present in directories under JCBloc. If you choose not to use
the Eclipse platform (or you just want to look at the code), you can
extract/view the files from these directories. The following program
description makes reference to these files.
PROGRAM DESCRIPTION:
The Android launcher initiates the app by running an instance of class
JCBlocActivity. This object displays the initial screen, which contains
four buttons and a text field. The top two buttons start and stop call
monitoring. The text field below them indicates which state is currently
active. The bottom two buttons initiate editing of the two lists maintained
by the program: a blacklist and a received calls list.
The JCBlocActivity onCreate() method initially either opens or creates an
SQlite database. A query is then made to determine which occurred. If it
was a create operation, the method creates two tables in the database:
blacklistTable and receivedCallsTable. It then loads an initial record into
table blacklistTable (for programming convenience, at least one entry must
be present in this table).
The startMonitorService() method, which is initiated by the "Start call
monitoring" button, then constructs an ArrayList<String> list and loads it
with the telephone numbers of all currently existing records in table
blacklistTable. This list will be passed to the JCBlocService() instance
when it is started. Since access to the SQlite database can be slow and
the number of entries in the blacklist can be large (typically one hundred
or more), the list is generated initially so that the Service can access
call numbers quickly. Android method startService() is then called to start
an instance of JCBlockService. Its argument is an Intent containing the
list.
The "Stop call monitoring" button initiates method stopMonitorService().
This in turn calls Android method stopService(), which terminates the
instance of JCBlockService. The JCBlocActivity class also contains the
button handlers for the blacklist and received calls list edit operations.
When the JCBlocService instance starts, its Android onCreate() method
creates an instance of an extended BroadcastReceiver() class and registers
it with an intentFilter specifying PHONE_STATE as the action to be
monitored. Callback Android method onStartCommand() then reads the call
numbers list into a local ArrayList<String> structure. This method returns
a START_STICKY value so that the Service will continue running until it
is explicitly stopped (in case the user switches to another app).
The onStartCommand() method calls local method startRunning() before
exiting. This method constructs a notification object that is passed as an
argument to the call to startForeground(). This call raises the priority
of the service so that it will compete successfully for run time even if
the user initiates other apps. Likewise, the Android callback onDestroy()
calls local method stopRunning() which, in turn, calls Android
stopForeground().
The extended BroadcastReceiver class, PhoneCallReceiver(), overrides
the onReceive() method. This method is run by the system when there is a
PHONE_STATE event. This is where the ITelephony methods are used to
control and monitor a call. The program needs to get access to an
ITelephony object, part of the TelephoneManager system service. Since
it is not directly available, it is accessed using the Java reflection
procedure.
The method first calls getSystemService() to retrieve a reference to an
instance of the TelephonyManager class, telephony. This object provides
access to information about the telephony services on the device. A Class
object representing the telephony class is then fetched. It is used to get
a Method object representing the getITelephony() method. Its invoke()
method is then used to fetch an ITelephony object.
A Bundle object is used to collect extra data contained in an Intent
delivered by onReceive() as an argument. The Bundle contains two strings:
one containing the call number; the other contains the state event that
caused onReceive() to be run. There are three possible values for the state
event: IDLE, OFFHOOK and RINGING. Most of the action takes place when a
RINGING event is reported.
First, the number of the incoming call is fetched from the bundle. Then
the current date and time are collected for use in constructing database
entries for the call. The call number is then compared to the numbers in
the ArrayList<String> list delivered in an Intent when the Service was
started. If a match is found, method silenceRinger() is called and, after
a delay, method endCall() is called to terminate the call. During this
process the phone rings once (a short ring). If the user has voicemail
enabled, the caller is directed to leave a message (most telemarketers
don't). Finally, method notifyUser() is called. It sends a Notification
message to the user containing call number, call date and time and whether
the call was blocked or not.
The last onReceive() callback for a call is for the IDLE state. When this
arrives, the data collected for the call is used to insert a record into
database table receivedCallsTable. At this point, the time required to
insert the single record is not critical.
Notifications:
As indicated, two notification messages are issued. The first informs the
user that call monitoring is active. the second indicates that a call was
received and processed. Both notifications display the project icon (a
red crossed circle overlaying a bordered capital "T"). The second has a
red dot superimposed over the lower-right corner with a number in it. This
count represents the number of calls that have been received since call
monitoring started.
If the user drags the notification drawer down and taps the "JCBloc
activity" entry, an Activity will display the data collected for the call.
If the Status: data item shows "was blocked", a button will be present to
allow the user to remove the call from the blacklist. Alternatively, if it
shows "not blocked", a button will be available to add it to the blacklist.
In either case, the user is prompted to stop and restart call monitoring in
order for the change to take effect. This is necessary to allow an updated
list of blacklist numbers to be sent to a restarted JCBlocService. Note
that the user need not press either button. He/she may just return to the
initial display. The processing is performed by an instance of the
NotifyMessage class.
Call database record editing:
As indicated, the lower two buttons on the start-up display provide a means
to edit records in database tables blacklistTable and receivedCallsTable.
The blacklistTable records contain a Comment field that may be used to add
a caller ID to the record. A means must also be provided to remove and
manually add records. Likewise the receivedCallsTable will grow without
bounds if a method is not provided to remove records.
Button handler editBlacklist() in class JCBlocActivity starts an instance
of ListActivity BlacklistEdit located in file BlacklistEdit.java. This
Activity displays a list of blacklistTable records and provides a means to
edit table records. The onCreate() method calls method loadEntryList(). It
constructs an ArrayList<entry> list, where 'entry' is a locally defined
class containing the data fields of a blacklistTable record. It then
populates the list with data from the table. The BlacklistAdapter class
manages the scrollable display of the list. This is accomplished by calling
method setListAdapter() with an instance of the BlacklistAdapter class as
its argument.
When the user taps a list entry, BlacklistEdit method onListItemClick() is
called. It starts an instance of Activity EditBlacklistEntries using a call
to its startActivityForResult() method. An override of class method
onActivityResult() is then provided to collect the results from this
Activity.
Class EditBlacklistEntries provides a way for the user to: 1)change the
comment field in an entry, 2)remove an entry altogether and 3)add a new
entry. It does this by means of TextView, EditText and Button widgets.
Results are returned to the BlacklistEdit onActivityResult() method via
included Intent objects. This method, in turn, uses the returned data to
make changes to database table blacklistTable and reconstruct and redisplay
the entry list. The user is then notified that, for the changes to take
effect, call monitoring must be stopped and restarted.
Classes RcvdCallEdit and EditRcvdCallListEntries perform similar operations
for editing receivedCallsTable records. A scrollable list of records is
displayed and the user may tap an entry to edit the corresponding record.
If a record for a call that was blocked is tapped, a button is displayed
that may be used to remove that record from table receivedCallsTable. For a
call that was not blocked, the user sees an additional button that may be
used to add the call record to table blacklistTable. In either case a text
field reminds the user that receivedCallsTable entries must be edited
periodically or the number or records in the list will grow without bound.
TO-DO SUGGESTIONS:
A number of additional features could be added to the app. A similar
program written to monitor landline calls using a modem incorporates them
(program: jcblock, also available from SourceForge.net. Enter: jcblock in
its Search window):
Add a whitelist: Implemented almost identically to the blacklist, a
whitelist holds records of calls the user definitely wants to accept. It
would be scanned first. If a received call's number is found, the blacklist
is not even consulted. The user could then be given the option to block all
calls NOT on the whitelist.
Add automatic table editing: About once a month scan the receivedCallsTable
for entries that are older than a month (or some other value) and remove
them. For the blacklist, the dateTime field could be changed to hold the
dateTime when the entry was last used to block a call. Then it could be
scanned once a month for records that have not been used to block a call
for (say) over a year. Those records could then be removed.
Add caller ID support: Unlike landline calls, caller ID is not directly
delivered by cellphones (for landline calls caller ID is delivered along
with other call data in ASCII strings between the first and second rings).
Various techniques have been used to collect it, generally through some web
reverse directory lookup. So the operation is not trivial. Using caller ID
instead of call number to identify calls to block has definite advantages.
Most telemarketers use several call numbers but only one caller ID.
Support more recent Android versions: If Android eventually provides a
non-hidden replacement for package ITelephony and/or decides to unblock
permission MODIFY_PHONE_STATE, convert the program to use the new telephony
interface (or convert the program to use some other app framework, e.g.,
keep an eye on these projects: Replicant, SHR, CyanogenMod, etc.). It
may be possible to get past the MODIFY_PHONE_STATE block by rooting a
phone and running the program from the System directory. I haven't
tried this yet.
For those that want a real challange: Figure out a way to get around
the block on permission MODIFY_HOME_STATE. Hint: I grep'd the source
and found one reference in some C++ code (the context didn't help much).
Recently Android withdrew online search of the source. You may find a
copy someplace else or have to download it (that's still available).
Make additional changes: This app has been a challange to write and a good
vehicle for learning the Android API. Use it to advance your skills and
understanding of this technology.
REFERENCES:
A good way to get answers to puzzles that arise is to use Google with a
search string something like:
Android <method or class name, etc.>
or
Android <error message text string>
You will find that most issues have come up before and have (probably) been
solved. These searches will often lead to site: stackoverflow.com. Lots of
good advice is located there. Another good site is:
http://developer.android.com/index.html
Enter a class or method name into its search window. Sometimes it is then
convenient to do a 'find' (Ctrl-F) to get to the specific information you
need.
I used the following books, in the order shown, to learn Android (I had
used Java, C, C++, etc. before):
"Sams Teach Yourself Android Application Development", by Lauren Darcey and
Shane Conder, Sams Publishing.
"Android Apps for Absolute Beginners", by Wallace Jackson, Apress.
"Beginning Android 3", Mark Murphy, Apress.
A Java reference:
"Sams Teach Yourself Java 6 in 21 Days", by Rogers Cadenhead and Laura
Lemay, Sams Publishing.
ALTERNATIVES:
There are at least a dozen "call filter" apps in the Google Market. In a
quick look I did not see any with source code available. No doubt most
(maybe all) of these are now broken for Android vesions newer than 2.2,
due to the block on MODIFY_PHONE_STATE.
MOTIVATION:
An item posted on the Slashdot website indicated that a bill is (at the
time of this writing) working its way through the US Congress that would
allow businesses to contact their customers via cellphone (google: HR3035).
Considering the impressive list of sponsors, something like it is likely to
pass. Opponents suspect that it is a back-door attempt by telemarketers to
get access to cellphone users. The item was the motivation for starting
this project.
Walter S. Heath, walmarheath@comcast.net