Home | History | Annotate | Download | only in service
      1 /*
      2  * Copyright (C) 2008 Esmertec AG.
      3  * Copyright (C) 2008 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.im.service;
     19 
     20 import java.util.concurrent.ExecutorService;
     21 import java.util.concurrent.Executors;
     22 
     23 import android.app.AlarmManager;
     24 import android.app.PendingIntent;
     25 import android.content.BroadcastReceiver;
     26 import android.content.ContentUris;
     27 import android.content.Context;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.net.Uri;
     31 import android.os.PowerManager;
     32 import android.os.SystemClock;
     33 import android.util.SparseArray;
     34 
     35 import com.android.im.engine.HeartbeatService;
     36 
     37 public class AndroidHeartBeatService extends BroadcastReceiver
     38         implements HeartbeatService {
     39 
     40     private static final String WAKELOCK_TAG = "IM_HEARTBEAT";
     41 
     42     private static final String HEARTBEAT_INTENT_ACTION
     43             = "com.android.im.intent.action.HEARTBEAT";
     44     private static final Uri HEARTBEAT_CONTENT_URI
     45             = Uri.parse("content://im/heartbeat");
     46     private static final String HEARTBEAT_CONTENT_TYPE
     47             = "vnd.android.im/heartbeat";
     48 
     49     private static final ExecutorService sExecutor = Executors.newSingleThreadExecutor();
     50 
     51     private final Context mContext;
     52     private final AlarmManager mAlarmManager;
     53     /*package*/ PowerManager.WakeLock mWakeLock;
     54 
     55     static class Alarm {
     56         public PendingIntent mAlaramSender;
     57         public Callback mCallback;
     58     }
     59 
     60     private final SparseArray<Alarm> mAlarms;
     61 
     62     public AndroidHeartBeatService(Context context) {
     63         mContext = context;
     64         mAlarmManager = (AlarmManager)context.getSystemService(
     65                 Context.ALARM_SERVICE);
     66         PowerManager powerManager = (PowerManager)context.getSystemService(
     67                 Context.POWER_SERVICE);
     68         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
     69                 WAKELOCK_TAG);
     70         mAlarms = new SparseArray<Alarm>();
     71     }
     72 
     73     public synchronized void startHeartbeat(Callback callback, long triggerTime) {
     74         Alarm alarm = findAlarm(callback);
     75         if (alarm == null) {
     76             alarm = new Alarm();
     77             int id = nextId();
     78             alarm.mCallback = callback;
     79             Uri data = ContentUris.withAppendedId(HEARTBEAT_CONTENT_URI, id);
     80             Intent i = new Intent(HEARTBEAT_INTENT_ACTION)
     81                             .setDataAndType(data, HEARTBEAT_CONTENT_TYPE);
     82             alarm.mAlaramSender = PendingIntent.getBroadcast(mContext, 0, i, 0);
     83             if (mAlarms.size() == 0) {
     84                 mContext.registerReceiver(this, IntentFilter.create(
     85                         HEARTBEAT_INTENT_ACTION, HEARTBEAT_CONTENT_TYPE));
     86             }
     87             mAlarms.append(id, alarm);
     88         }
     89         setAlarm(alarm, triggerTime);
     90     }
     91 
     92     public synchronized void stopHeartbeat(Callback callback) {
     93         Alarm alarm = findAlarm(callback);
     94         if (alarm != null) {
     95             cancelAlarm(alarm);
     96         }
     97     }
     98 
     99     public synchronized void stopAll() {
    100         for (int i = 0; i < mAlarms.size(); i++) {
    101             Alarm alarm = mAlarms.valueAt(i);
    102             cancelAlarm(alarm);
    103         }
    104     }
    105 
    106     @Override
    107     public void onReceive(Context context, Intent intent) {
    108         int id = (int)ContentUris.parseId(intent.getData());
    109         Alarm alarm = mAlarms.get(id);
    110         if (alarm == null) {
    111             return;
    112         }
    113         sExecutor.execute(new Worker(alarm));
    114     }
    115 
    116     private class Worker implements Runnable {
    117         private final Alarm mAlarm;
    118 
    119         public Worker(Alarm alarm) {
    120             mAlarm = alarm;
    121         }
    122 
    123         public void run() {
    124             mWakeLock.acquire();
    125             try {
    126                 Callback callback = mAlarm.mCallback;
    127                 long nextSchedule = callback.sendHeartbeat();
    128                 if (nextSchedule <= 0) {
    129                     cancelAlarm(mAlarm);
    130                 } else {
    131                     setAlarm(mAlarm, nextSchedule);
    132                 }
    133             } finally {
    134                 mWakeLock.release();
    135             }
    136         }
    137     }
    138 
    139     private Alarm findAlarm(Callback callback) {
    140         for (int i = 0; i < mAlarms.size(); i++) {
    141             Alarm alarm = mAlarms.valueAt(i);
    142             if (alarm.mCallback == callback) {
    143                 return alarm;
    144             }
    145         }
    146         return null;
    147     }
    148 
    149     /*package*/ synchronized void setAlarm(Alarm alarm, long offset) {
    150         long triggerAtTime = SystemClock.elapsedRealtime() + offset;
    151         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime,
    152                 alarm.mAlaramSender);
    153     }
    154 
    155     /*package*/  synchronized void cancelAlarm(Alarm alarm) {
    156         mAlarmManager.cancel(alarm.mAlaramSender);
    157         int index = mAlarms.indexOfValue(alarm);
    158         if (index >= 0) {
    159             mAlarms.delete(mAlarms.keyAt(index));
    160         }
    161 
    162         // Unregister the BroadcastReceiver if there isn't a alarm anymore.
    163         if (mAlarms.size() == 0) {
    164             mContext.unregisterReceiver(this);
    165         }
    166     }
    167 
    168     private static int sNextId = 0;
    169     private static synchronized int nextId() {
    170         return sNextId++;
    171     }
    172 }
    173