Source code for Long Running Broadcast Service

Respond to a broadcast event and do the work even if it takes more than 10 secs with out raising a "not responding" message.

Use the IntentService as the core facility to do this

Get a wake lock so that the device is kept partially on.

Allow for the fact that the service can be reclaimed and also brought back

Allow for multiple receivers and services in the same apk file

Hide the details of broadcast and the service as much as possible and make it look like as if you are handling a broadcast event.

you will see all the necessary source files and the manifest file to demonstrate the goals. You will also see the log file entries to see how many threads are running and how the locks are done and how the services demonstrate handling long running needs.


public class Test60SecBCRService 
extends ALongRunningNonStickyBroadcastService
{
   public static String tag = "Test60SecBCRService";
   
   //Required by IntentService
   public Test60SecBCRService()
   {
      super("com.ai.android.service.Test60SecBCRService");
   }
   
   /*
    * Perform long running operations in this method.
    * This is executed in a separate thread. 
    */
   @Override
   protected void handleBroadcastIntent(Intent broadcastIntent) 
   {
      Utils.logThreadSignature();
      Log.d(tag,"Sleeping for 60 secs");
      Utils.sleepForInSecs(60);
      String message = 
         broadcastIntent.getStringExtra("message");
      Log.d(tag,"Job completed");
      Log.d(tag,message);
   }
}

public class Test60SecBCR
extends ALongRunningReceiver
{
   @Override
   public Class getLRSClass() 
   {
      Utils.logThreadSignature();
      return Test60SecBCRService.class;
   }
}

You will need to examine the following files


ALongRunningReceiver
ALongRunningNonStickyBroadcastService
LighedGreenRoom

/*
 * This is a stand-in broadcast receiver
 * that delegates the work to a service
 * named by the derived class.
 * 
 * The original intent that the broadcast receiver
 * is invoked on is transferred to the 
 * delegated non-sticky service to be handled
 * as a parcellable.
 * 
 * On entry this will set up the 
 * lighted green room. Essentially making the device
 * on.
 * 
 * The service will do the same, if it were to 
 * be woken up due to pending intents.
 */
public abstract class  ALongRunningReceiver 
extends BroadcastReceiver 
{
   private static final String tag = "ALongRunningReceiver"; 
    @Override
    public void onReceive(Context context, Intent intent) 
    {
       Log.d(tag,"Receiver started");
       LightedGreenRoom.setup(context);
       startService(context,intent);
       Log.d(tag,"Receiver finished");
    }
    
    private void startService(Context context, Intent intent)
    {
       Intent serviceIntent = new Intent(context,getLRSClass());
       serviceIntent.putExtra("original_intent", intent);
       context.startService(serviceIntent);
    }
    /*
     * Override this methode to return the 
     * "class" object belonging to the 
     * nonsticky service class.
     */
    public abstract Class getLRSClass();
    
}

public abstract class ALongRunningNonStickyBroadcastService extends IntentService
{
   public static String tag = "ALongRunningBroadcastService";
   protected abstract void 
   handleBroadcastIntent(Intent broadcastIntent);
   
   public ALongRunningNonStickyBroadcastService(String name) 
   {      
      super(name);   
   }   

   /*
    * This method can be invoked under two circumstances
    * 1. When a broadcast receiver issues a "startService"
    * 2. when android restarts it due to pending "startService" intents.
    * 
    * In case 1, the broadcast receiver has already
    * setup the "lightedgreenroom".
    * 
    * In case 2, we need to do the same.
    */
   @Override
   public void onCreate() 
   {
      super.onCreate();
      
      //Set up the green room
      //The setup is capable of getting called multiple times.
      LightedGreenRoom.setup(this.getApplicationContext());

      //It is possible that there are more than one service 
      //of this type is running. 
      //Knowing the number will allow us to clean up 
      //the locks in ondestroy.
      LightedGreenRoom.s_registerClient();
   }
   
   @Override
   public int onStartCommand(Intent intent, int flag, int startId) 
   {
      //Call the IntetnService "onstart"
      super.onStart(intent, startId);
      
      //Tell the green room there is a visitor
      LightedGreenRoom.s_enter();
      
      //mark this as non sticky
      //Means: Don't restart the service if there are no
      //pending intents.
      return Service.START_NOT_STICKY;
      
   }

   /*
    * Note that this method call runs
    * in a secondary thread setup by the IntentService.
    * 
    * Override this method from IntentService.
    * Retrieve the original broadcast intent.
    * Call the derived class to handle the broadcast intent.
    * finally tell the ligthed room that you are leaving.
    * if this is the last visitor then the lock 
    * will be released.
    */
   @Override   
   final protected void onHandleIntent(Intent intent) 
   {      
      try 
      {         
         Intent broadcastIntent
         = intent.getParcelableExtra("original_intent");
         handleBroadcastIntent(broadcastIntent);
      }      
      finally 
      {
         LightedGreenRoom.s_leave();
      }   
   }
   /*
    * If Android reclaims this process,
    * this method will release the lock 
    * irrespective of how many visitors there are.
    */
   @Override
   public void onDestroy() {
      super.onDestroy();
      LightedGreenRoom.s_unRegisterClient();
   }

}

/*
 * What is a Lighted Green Room?
 * ****************************************************
 * A green room is a room that allows visitors.
 * A green room starts out dark.
 * The first one to "enter" turn on the lights.
 * Subsequent visitors have no effect if the lights
 * are already on.
 * The last visitor to leave will turn off the lights.
 * The methods are synchronized to keep state.
 * "enter" and "leave" could happen between multiple threads.
 * It is called a green room because it uses energy efficiently.
 * Lame, but I think gets the point across.
 * 
 * What is a lighted green room
 * ****************************************************
 * Unlike a green room that starts out lights off,
 * a lighted green room starts out with the lights on.
 * The last one to leave will turn off the lights.
 * 
 * This will be useful if there are two entry points. 
 * If one of the entry points is a delayed entry, 
 * the room not knowing that someone is scheduled to come,
 * might turn the lights off.
 * 
 * The control is taken away from the one that is "entering"
 * the room.
 * Instead the room is already lighted.
 * The "enter" is only tracked to see if the 
 * last one has exited.
 * 
 */
public class LightedGreenRoom 
{
   //debug tag
   private static String tag="LightedGreenRoom";
   
   //Keep count of visitors to know the last visitor.
   //On destory set the count to zero to clear the room.
   private int count;
   
   //Needed to create the wake lock
   private Context ctx = null;
   
   //Our switch
   PowerManager.WakeLock wl = null;
   
   //Multi-client support
   private int clientCount = 0;
   
   /*
    * This is expected to be a singleton.
    * One could potentially make the constructor
    * private I suppose.
    */
   public LightedGreenRoom(Context inCtx)
   {
      ctx = inCtx;
      wl = this.createWakeLock(inCtx);
   }
   
   /*
    * Setting up the green room using a static method.
    * This has to be called before calling any other methods.
    * what it does:
    *       1. Instantiate the object
    *       2. acquire the lock to turn on lights
    * Assumption:
    *       It is not required to be synchronized
    *       because it will be called from the main thread.
    *       (Could be wrong. need to validate this!!)
    */
   private static LightedGreenRoom s_self = null;
   
   public static void setup(Context inCtx)
   {
      if (s_self == null)
      {
         Log.d(LightedGreenRoom.tag,"Creating green room and lighting it");
         s_self = new LightedGreenRoom(inCtx);
         s_self.turnOnLights();
      }
   }
   /*
    * Not sure if I need this
    */
   public static boolean isSetup()
   {
      return (s_self != null) ? true: false; 
   }

   /*
    * The methods "enter" and "leave" are 
    * expected to be called in tandem.
    * 
    * On "enter" increment the count.
    * 
    * Do not turn the lights or off 
    * as they are already turned on.
    * 
    * Just increment the count to know
    * when the last visitor leaves.
    * 
    * This is a synchronized method as 
    * multiple threads will be entering and leaving.
    * 
    */
   synchronized public int enter()
   {
      count++;
      Log.d(tag,"A new visitor: count:" + count);
      return count;
   }
   /*
    * The methods "enter" and "leave" are 
    * expected to be called in tandem.
    * 
    * On "leave" decrement the count.
    * 
    * If the count reaches zero turn off the lights. 
    * 
    * This is a synchronized method as 
    * multiple threads will be entering and leaving.
    * 
    */
   synchronized public int leave()
   {
      Log.d(tag,"Leaving room:count at the call:" + count);
      //if the count is already zero
      //just leave.
      if (count == 0) 
      {
         Log.w(tag,"Count is zero.");
         return count;
      }
      count--;
      if (count == 0)
      {
         //Last visitor
         //turn off lights
         turnOffLights();
      }
      return count;
   }
   /*
    * Right now no one is using this method.
    * Just in case.
    */
   synchronized public int getCount()
   {
      return count;
   }
   
   /*
    * acquire the wake lock to turn the lights on
    * it is upto other synchronized methods to call 
    * this at the appropriate time.
    */
   private void turnOnLights()
   {
      Log.d(tag, "Turning on lights. Count:" + count);
      this.wl.acquire();
   }
   
   /*
    * Release the wake lock to turn the lights off.
    * it is upto other synchronized methods to call 
    * this at the appropriate time.
    */
   private void turnOffLights()
   {
      if (this.wl.isHeld())
      {
         Log.d(tag,"Releasing wake lock. No more visitors");
         this.wl.release();
      }
   }
   /*
    * Standard code to create a partial wake lock
    */
   private PowerManager.WakeLock createWakeLock(Context inCtx)
   {
      PowerManager pm = 
         (PowerManager)inCtx.getSystemService(Context.POWER_SERVICE); 

      PowerManager.WakeLock wl = pm.newWakeLock
           (PowerManager.PARTIAL_WAKE_LOCK, tag);
      return wl;
   }
   
   private int registerClient()
   {
      Utils.logThreadSignature();
      this.clientCount++;
      Log.d(tag,"registering a new client:count:" + clientCount);
      return clientCount;
   }
   
   private int unRegisterClient()
   {
      Utils.logThreadSignature();
      Log.d(tag,"un registering a new client:count:" + clientCount);
      if (clientCount == 0)
      {
         Log.w(tag,"There are no clients to unregister.");
         return 0;
      }
      //clientCount is not zero
      clientCount--;
      if (clientCount == 0)
      {
         emptyTheRoom();
      }
      return clientCount;
   }
   synchronized public void emptyTheRoom()
   {
      Log.d(tag, "Call to empty the room");
      count = 0;
      this.turnOffLights();
   }
   //*************************************************
   //*static members: Purely helper methods
   //*   Delegates to the underlying singleton object
   //*************************************************
   public static int s_enter()
   {
      assertSetup();
      return s_self.enter();
   }
   public static int s_leave()
   {
      assertSetup();
      return s_self.leave();
   }
   //Dont directly call this method
   //probably will be deprecated.
   //Call register and unregister client methods instead
   public static void ds_emptyTheRoom()
   {
      assertSetup();
      s_self.emptyTheRoom();
      return;
   }
   public static void s_registerClient()
   {
      assertSetup();
      s_self.registerClient();
      return;
   }
   public static void s_unRegisterClient()
   {
      assertSetup();
      s_self.unRegisterClient();
      return;
   }
   private static void assertSetup()
   {
      if (LightedGreenRoom.s_self == null)
      {
         Log.w(LightedGreenRoom.tag,"You need to call setup first");
         throw new RuntimeException("You need to setup GreenRoom first");
      }
   }
   
}

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.ai.android.salbcr"
      android:versionCode="1"
      android:versionName="1.0.0">

    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application android:icon="@drawable/icon" 
               android:label="Standalone Broadcast Receiver">

<!--
************************************************************
* A receiver in a process outside of the 
* broadcast sender. 
************************************************************ 
-->   
    <receiver android:name=".StandaloneReceiver">
       <intent-filter>
          <action android:name="com.ai.android.intents.testbc"/>
       </intent-filter>
    </receiver>
    
<!--
************************************************************
* A receiver to prove notifications 
************************************************************ 
-->   
    <receiver android:name=".NotificationReceiver">
       <intent-filter>
          <action android:name="com.ai.android.intents.testbc"/>
       </intent-filter>
    </receiver>
    
<!--
************************************************************
* Long Running Receiver Basic Test 
************************************************************ 
-->   
    <receiver android:name=".TestLRBCR">
       <intent-filter>
          <action android:name="com.ai.android.intents.testbc"/>
       </intent-filter>
    </receiver>

   <service android:name=".TestLRBCRService"/>

<!--
************************************************************
* Long Running Receiver 30 sec slepp Test 
************************************************************ 
-->   
    <receiver android:name=".Test30SecBCR">
       <intent-filter>
          <action android:name="com.ai.android.intents.testbc"/>
       </intent-filter>
    </receiver>
   <service android:name=".Test30SecBCRService"/>

<!--
************************************************************
* Long Running Receiver 60 sec sleep Test 
************************************************************ 
-->   
    <receiver android:name=".Test60SecBCR">
       <intent-filter>
          <action android:name="com.ai.android.intents.testbc"/>
       </intent-filter>
    </receiver>
   <service android:name=".Test60SecBCRService"/>
   
</application>
    <uses-sdk android:minSdkVersion="3" />
</manifest>

Test30SecBCR.java/Test30SecBCRService.java
TestLRBCR.java/TestLRBCRService.java

public class Test30SecBCR
extends ALongRunningReceiver
{
   @Override
   public Class getLRSClass() 
   {
      Utils.logThreadSignature();
      return Test30SecBCRService.class;
   }
}

public class Test30SecBCRService 
extends ALongRunningNonStickyBroadcastService
{
   public static String tag = "Test30SecBCRService";
   
   //Required by IntentService
   public Test30SecBCRService()
   {
      super("com.ai.android.service.Test30SecBCRService");
   }
   
   /*
    * Perform long running operations in this method.
    * This is executed in a separate thread. 
    */
   @Override
   protected void handleBroadcastIntent(Intent broadcastIntent) 
   {
      Utils.logThreadSignature();
      Log.d(tag,"Sleeping for 30 secs");
      Utils.sleepForInSecs(30);
      String message = 
         broadcastIntent.getStringExtra("message");
      Log.d(tag,"Job completed");
      Log.d(tag,message);
   }
}

public class TestLRBCR
extends ALongRunningReceiver
{
   @Override
   public Class getLRSClass() 
   {
      Utils.logThreadSignature();
      return TestLRBCRService.class;
   }
}

/*
 * Uses IntentService as the base class
 * to make this work on a separate thread.
 */
public class TestLRBCRService 
extends ALongRunningNonStickyBroadcastService
{
   public static String tag = "TestLRBCRService";
   
   //Required by IntentService
   public TestLRBCRService()
   {
      super("com.ai.android.service.TestLRBCRService");
   }
   
   /*
    * Perform long running operations in this method.
    * This is executed in a separate thread. 
    */
   @Override
   protected void handleBroadcastIntent(Intent broadcastIntent) 
   {
      Utils.logThreadSignature();
      String message = 
         broadcastIntent.getStringExtra("message");
      Log.d(tag,message);
   }
}

public class Utils 
{
   public static long getThreadId()
   {
      Thread t = Thread.currentThread();
      return t.getId();
   }

   public static String getThreadSignature()
   {
      Thread t = Thread.currentThread();
      long l = t.getId();
      String name = t.getName();
      long p = t.getPriority();
      String gname = t.getThreadGroup().getName();
      return (name + ":(id)" + l + ":(priority)" + p
            + ":(group)" + gname);
   }
   
   public static void logThreadSignature()
   {
      Log.d("ThreadUtils", getThreadSignature());
   }
   
   public static void sleepForInSecs(int secs)
   {
      try
      {
         Thread.sleep(secs * 1000);
      }
      catch(InterruptedException x)
      {
         throw new RuntimeException("interrupted",x);
      }
   }
}

06-11 03:07:01.901: DEBUG/ThreadUtils(242): main:(id)1:(priority)5:(group)main
06-11 03:07:01.941: DEBUG/HelloWorld(242): after send broadcast from main menu
06-11 03:07:01.981: INFO/ActivityManager(59): Start proc com.ai.android.handlers 
for broadcast com.ai.android.handlers/.TestReceiver: pid=260 uid=10035 gids={1015}
06-11 03:07:02.282: WARN/InputManagerService(59): Window already focused, ignoring 
focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@43ab9020
06-11 03:07:02.941: DEBUG/ThreadUtils(260): main:(id)1:(priority)5:(group)main
06-11 03:07:02.961: DEBUG/TestReceiver(260): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.handlers/.TestReceiver (has extras) }
06-11 03:07:02.970: DEBUG/TestReceiver(260): Hello world
06-11 03:07:03.010: DEBUG/ThreadUtils(260): main:(id)1:(priority)5:(group)main
06-11 03:07:03.020: DEBUG/TestTimeDelayReceiver(260): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.handlers/.TestTimeDelayReceiver (has extras) }
06-11 03:07:03.031: DEBUG/TestTimeDelayReceiver(260): going to sleep for 2 secs
06-11 03:07:03.091: DEBUG/ddm-heap(260): Got feature list request
06-11 03:07:05.047: DEBUG/TestTimeDelayReceiver(260): wake up
06-11 03:07:05.047: DEBUG/TestTimeDelayReceiver(260): Hello world
06-11 03:07:05.121: DEBUG/ThreadUtils(260): main:(id)1:(priority)5:(group)main
06-11 03:07:05.131: DEBUG/TestReceiver2(260): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.handlers/.TestReceiver2 (has extras) }
06-11 03:07:05.141: DEBUG/TestReceiver2(260): Hello world
06-11 03:07:05.241: DEBUG/ThreadUtils(242): main:(id)1:(priority)5:(group)main
06-11 03:07:05.251: DEBUG/TestReceiver(242): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.bcr/.TestReceiver (has extras) }
06-11 03:07:05.271: DEBUG/TestReceiver(242): Hello world
06-11 03:07:05.301: DEBUG/ThreadUtils(242): main:(id)1:(priority)5:(group)main
06-11 03:07:05.312: DEBUG/TestTimeDelayReceiver(242): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.bcr/.TestTimeDelayReceiver (has extras) }
06-11 03:07:05.321: DEBUG/TestTimeDelayReceiver(242): going to sleep for 2 secs
06-11 03:07:07.337: DEBUG/TestTimeDelayReceiver(242): wake up
06-11 03:07:07.337: DEBUG/TestTimeDelayReceiver(242): Hello world
06-11 03:07:07.372: DEBUG/ThreadUtils(242): main:(id)1:(priority)5:(group)main
06-11 03:07:07.381: DEBUG/TestReceiver2(242): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.bcr/.TestReceiver2 (has extras) }
06-11 03:07:07.403: DEBUG/TestReceiver2(242): Hello world
06-11 03:07:07.461: INFO/ActivityManager(59): Start proc com.ai.android.salbcr for 
broadcast com.ai.android.salbcr/.StandaloneReceiver: pid=266 uid=10036 gids={1015}
06-11 03:07:08.131: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:08.151: DEBUG/Standalone Receiver(266): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.salbcr/.StandaloneReceiver (has extras) }
06-11 03:07:08.151: DEBUG/Standalone Receiver(266): Hello world
06-11 03:07:08.201: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:08.211: DEBUG/Notification Receiver(266): intent=Intent 
{ act=com.ai.android.intents.testbc cmp=com.ai.android.salbcr/.NotificationReceiver (has extras) }
06-11 03:07:08.231: DEBUG/Notification Receiver(266): Hello world
06-11 03:07:09.021: DEBUG/ALongRunningReceiver(266): Receiver started
06-11 03:07:09.031: DEBUG/LightedGreenRoom(266): Creating green room and lighting it
06-11 03:07:09.113: DEBUG/LightedGreenRoom(266): Turning on lights. Count:0
06-11 03:07:09.121: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.301: DEBUG/ALongRunningReceiver(266): Receiver finished
06-11 03:07:09.391: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.421: DEBUG/LightedGreenRoom(266): registering a new client:count:1
06-11 03:07:09.421: DEBUG/LightedGreenRoom(266): A new visitor: count:1
06-11 03:07:09.491: DEBUG/ALongRunningReceiver(266): Receiver started
06-11 03:07:09.491: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.550: DEBUG/ALongRunningReceiver(266): Receiver finished
06-11 03:07:09.731: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.731: DEBUG/LightedGreenRoom(266): registering a new client:count:2
06-11 03:07:09.741: DEBUG/LightedGreenRoom(266): A new visitor: count:2
06-11 03:07:09.791: DEBUG/ALongRunningReceiver(266): Receiver started
06-11 03:07:09.791: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.880: DEBUG/ALongRunningReceiver(266): Receiver finished
06-11 03:07:09.920: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:09.920: DEBUG/LightedGreenRoom(266): registering a new client:count:3
06-11 03:07:09.991: DEBUG/LightedGreenRoom(266): A new visitor: count:3
06-11 03:07:10.351: DEBUG/ThreadUtils(266): IntentService
[com.ai.android.service.Test60SecBCRService]:(id)10:(priority)5:
(group)main
06-11 03:07:10.361: DEBUG/Test60SecBCRService(266): Sleeping for 60 secs
06-11 03:07:10.403: DEBUG/ThreadUtils(266): IntentService[com.ai.android.service.Test30SecBCRService]:
(id)9:(priority)5:(group)main
06-11 03:07:10.403: DEBUG/Test30SecBCRService(266): Sleeping for 30 secs
06-11 03:07:10.411: DEBUG/ThreadUtils(266): IntentService[com.ai.android.service.TestLRBCRService]:
(id)8:(priority)5:(group)main
06-11 03:07:10.433: DEBUG/TestLRBCRService(266): Hello world
06-11 03:07:10.433: DEBUG/LightedGreenRoom(266): Leaving room:count at the call:3
06-11 03:07:10.450: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:10.450: DEBUG/LightedGreenRoom(266): un registering a new client:count:3
06-11 03:07:10.631: DEBUG/ddm-heap(266): Got feature list request
06-11 03:07:40.411: DEBUG/Test30SecBCRService(266): Job completed
06-11 03:07:40.473: DEBUG/Test30SecBCRService(266): Hello world
06-11 03:07:40.473: DEBUG/LightedGreenRoom(266): Leaving room:count at the call:2
06-11 03:07:40.520: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:07:40.531: DEBUG/LightedGreenRoom(266): un registering a new client:count:2
06-11 03:08:10.365: DEBUG/Test60SecBCRService(266): Job completed
06-11 03:08:10.431: DEBUG/Test60SecBCRService(266): Hello world
06-11 03:08:10.441: DEBUG/LightedGreenRoom(266): Leaving room:count at the call:1
06-11 03:08:10.441: DEBUG/LightedGreenRoom(266): Releasing wake lock. No more visitors
06-11 03:08:10.511: DEBUG/ThreadUtils(266): main:(id)1:(priority)5:(group)main
06-11 03:08:10.521: DEBUG/LightedGreenRoom(266): un registering a new client:count:1
06-11 03:08:10.521: DEBUG/LightedGreenRoom(266): Call to empty the room
06-11 03:08:15.931: DEBUG/dalvikvm(266): GC freed 4258 objects / 277024 bytes in 367ms

Activity activity;
Intent bi = new Intent(activity, Test30SecBCR.class);
//load intent "bi" with extras if you need
sendBroadCast(bi);

This would have resulted in invoking the corresponding Test30SecBCRService that can run for 30 seconds. The service can also extract the additional extras from the intent. It is as if you are doing


Activity activity;
Intent bi = new Intent(activity, Test30SecBCRService.class);
//load intent "bi" with extras if you need
sendBroadCast(bi);

AlarmManager mgr = (AlarmManager) getSystemService(ALARM_SERVICE);

//Prime the intent with our abstract receiver
Intent i = new Intent(this, Test30SecBCR.class);

//Get a pending intent
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);

//tell the alarm manager to invoke the pending intent
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
             SystemClock.elapsedRealtime(), (120000),
             pi);

Don't forget to browse the related docs on the top right of this page