• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AndroidHeartBeatService(Context context)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 
startHeartbeat(Callback callback, long triggerTime)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 
stopHeartbeat(Callback callback)92     public synchronized void stopHeartbeat(Callback callback) {
93         Alarm alarm = findAlarm(callback);
94         if (alarm != null) {
95             cancelAlarm(alarm);
96         }
97     }
98 
stopAll()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
onReceive(Context context, Intent intent)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 
Worker(Alarm alarm)119         public Worker(Alarm alarm) {
120             mAlarm = alarm;
121         }
122 
run()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 
findAlarm(Callback callback)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 
setAlarm(Alarm alarm, long offset)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 
cancelAlarm(Alarm alarm)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;
nextId()169     private static synchronized int nextId() {
170         return sNextId++;
171     }
172 }
173