blob: 659451fe710309826947d1a38eeae974457968b5 [file] [log] [blame]
/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.im.service;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.SparseArray;
import com.android.im.engine.HeartbeatService;
public class AndroidHeartBeatService extends BroadcastReceiver
implements HeartbeatService {
private static final String WAKELOCK_TAG = "IM_HEARTBEAT";
private static final String HEARTBEAT_INTENT_ACTION
= "com.android.im.intent.action.HEARTBEAT";
private static final Uri HEARTBEAT_CONTENT_URI
= Uri.parse("content://im/heartbeat");
private static final String HEARTBEAT_CONTENT_TYPE
= "vnd.android.im/heartbeat";
private static final ExecutorService sExecutor = Executors.newSingleThreadExecutor();
private final Context mContext;
private final AlarmManager mAlarmManager;
/*package*/ PowerManager.WakeLock mWakeLock;
static class Alarm {
public PendingIntent mAlaramSender;
public Callback mCallback;
}
private final SparseArray<Alarm> mAlarms;
public AndroidHeartBeatService(Context context) {
mContext = context;
mAlarmManager = (AlarmManager)context.getSystemService(
Context.ALARM_SERVICE);
PowerManager powerManager = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
WAKELOCK_TAG);
mAlarms = new SparseArray<Alarm>();
}
public synchronized void startHeartbeat(Callback callback, long triggerTime) {
Alarm alarm = findAlarm(callback);
if (alarm == null) {
alarm = new Alarm();
int id = nextId();
alarm.mCallback = callback;
Uri data = ContentUris.withAppendedId(HEARTBEAT_CONTENT_URI, id);
Intent i = new Intent(HEARTBEAT_INTENT_ACTION)
.setDataAndType(data, HEARTBEAT_CONTENT_TYPE);
alarm.mAlaramSender = PendingIntent.getBroadcast(mContext, 0, i, 0);
if (mAlarms.size() == 0) {
mContext.registerReceiver(this, IntentFilter.create(
HEARTBEAT_INTENT_ACTION, HEARTBEAT_CONTENT_TYPE));
}
mAlarms.append(id, alarm);
}
setAlarm(alarm, triggerTime);
}
public synchronized void stopHeartbeat(Callback callback) {
Alarm alarm = findAlarm(callback);
if (alarm != null) {
cancelAlarm(alarm);
}
}
public synchronized void stopAll() {
for (int i = 0; i < mAlarms.size(); i++) {
Alarm alarm = mAlarms.valueAt(i);
cancelAlarm(alarm);
}
}
@Override
public void onReceive(Context context, Intent intent) {
int id = (int)ContentUris.parseId(intent.getData());
Alarm alarm = mAlarms.get(id);
if (alarm == null) {
return;
}
sExecutor.execute(new Worker(alarm));
}
private class Worker implements Runnable {
private final Alarm mAlarm;
public Worker(Alarm alarm) {
mAlarm = alarm;
}
public void run() {
mWakeLock.acquire();
try {
Callback callback = mAlarm.mCallback;
long nextSchedule = callback.sendHeartbeat();
if (nextSchedule <= 0) {
cancelAlarm(mAlarm);
} else {
setAlarm(mAlarm, nextSchedule);
}
} finally {
mWakeLock.release();
}
}
}
private Alarm findAlarm(Callback callback) {
for (int i = 0; i < mAlarms.size(); i++) {
Alarm alarm = mAlarms.valueAt(i);
if (alarm.mCallback == callback) {
return alarm;
}
}
return null;
}
/*package*/ synchronized void setAlarm(Alarm alarm, long offset) {
long triggerAtTime = SystemClock.elapsedRealtime() + offset;
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime,
alarm.mAlaramSender);
}
/*package*/ synchronized void cancelAlarm(Alarm alarm) {
mAlarmManager.cancel(alarm.mAlaramSender);
int index = mAlarms.indexOfValue(alarm);
if (index >= 0) {
mAlarms.delete(mAlarms.keyAt(index));
}
// Unregister the BroadcastReceiver if there isn't a alarm anymore.
if (mAlarms.size() == 0) {
mContext.unregisterReceiver(this);
}
}
private static int sNextId = 0;
private static synchronized int nextId() {
return sNextId++;
}
}