3 import java
.io
.BufferedInputStream
;
4 import java
.io
.BufferedOutputStream
;
6 import java
.io
.FileNotFoundException
;
7 import java
.io
.FileOutputStream
;
8 import java
.io
.IOException
;
9 import java
.lang
.reflect
.InvocationTargetException
;
10 import java
.lang
.reflect
.Method
;
11 import java
.util
.Enumeration
;
12 import java
.util
.Timer
;
13 import java
.util
.TimerTask
;
14 import java
.util
.zip
.ZipEntry
;
15 import java
.util
.zip
.ZipFile
;
17 import android
.app
.Notification
;
18 import android
.app
.NotificationManager
;
19 import android
.app
.PendingIntent
;
20 import android
.app
.Service
;
21 import android
.content
.BroadcastReceiver
;
22 import android
.content
.Context
;
23 import android
.content
.Intent
;
24 import android
.content
.IntentFilter
;
25 import android
.os
.IBinder
;
26 import android
.util
.Log
;
28 public class RockboxService
extends Service
30 /* this Service is really a singleton class */
31 public static RockboxFramebuffer fb
= null;
32 private static RockboxService instance
;
33 private Notification notification
;
34 private static final Class
<?
>[] mStartForegroundSignature
= new Class
[] {
35 int.class, Notification
.class};
36 private static final Class
<?
>[] mStopForegroundSignature
= new Class
[] {
39 private NotificationManager mNM
;
40 private Method mStartForeground
;
41 private Method mStopForeground
;
42 private Object
[] mStartForegroundArgs
= new Object
[2];
43 private Object
[] mStopForegroundArgs
= new Object
[1];
44 private IntentFilter itf
;
45 private BroadcastReceiver batt_monitor
;
46 private int battery_level
;
48 public void onCreate()
50 mNM
= (NotificationManager
)getSystemService(NOTIFICATION_SERVICE
);
52 mStartForeground
= getClass().getMethod("startForeground",
53 mStartForegroundSignature
);
54 mStopForeground
= getClass().getMethod("stopForeground",
55 mStopForegroundSignature
);
56 } catch (NoSuchMethodException e
) {
57 /* Running on an older platform: fall back to old API */
58 mStartForeground
= mStopForeground
= null;
64 private void do_start(Intent intent
)
67 /* Display a notification about us starting. We put an icon in the status bar. */
68 create_notification();
71 private void LOG(CharSequence text
)
73 Log
.d("Rockbox", (String
) text
);
76 private void LOG(CharSequence text
, Throwable tr
)
78 Log
.d("Rockbox", (String
) text
, tr
);
81 public void onStart(Intent intent
, int startId
) {
85 public int onStartCommand(Intent intent
, int flags
, int startId
)
88 return 1; /* old API compatibility: 1 == START_STICKY */
91 private void startservice()
93 fb
= new RockboxFramebuffer(this);
94 final int BUFFER
= 2048;
95 /* the following block unzips libmisc.so, which contains the files
96 * we ship, such as themes. It's needed to put it into a .so file
97 * because there's no other way to ship files and have access
98 * to them from native code
102 BufferedOutputStream dest
= null;
103 BufferedInputStream is
= null;
105 File file
= new File("/data/data/org.rockbox/lib/libmisc.so");
106 /* use arbitary file to determine whether extracting is needed */
107 File file2
= new File("/data/data/org.rockbox/app_rockbox/rockbox/codecs/mpa.codec");
108 if (!file2
.exists() || (file
.lastModified() > file2
.lastModified()))
110 ZipFile zipfile
= new ZipFile(file
);
111 Enumeration
<?
extends ZipEntry
> e
= zipfile
.entries();
113 while(e
.hasMoreElements()) {
114 entry
= (ZipEntry
) e
.nextElement();
115 LOG("Extracting: " +entry
);
116 if (entry
.isDirectory())
118 folder
= new File(entry
.getName());
119 LOG("mkdir "+ entry
);
122 } catch (SecurityException ex
){
123 LOG(ex
.getMessage());
127 is
= new BufferedInputStream(zipfile
.getInputStream(entry
));
129 byte data
[] = new byte[BUFFER
];
130 folder
= new File(new File(entry
.getName()).getParent());
131 LOG("" + folder
.getAbsolutePath());
132 if (!folder
.exists())
134 FileOutputStream fos
= new FileOutputStream(entry
.getName());
135 dest
= new BufferedOutputStream(fos
, BUFFER
);
136 while ((count
= is
.read(data
, 0, BUFFER
)) != -1) {
137 dest
.write(data
, 0, count
);
144 } catch(FileNotFoundException e
) {
145 LOG("FileNotFoundException when unzipping", e
);
147 } catch(IOException e
) {
148 LOG("IOException when unzipping", e
);
152 System
.loadLibrary("rockbox");
154 Thread rb
= new Thread(new Runnable()
165 private native void main();
167 public IBinder
onBind(Intent intent
) {
168 // TODO Auto-generated method stub
173 @SuppressWarnings("unused")
175 * Sets up the battery monitor which receives the battery level
176 * about each 30 seconds
178 private void initBatteryMonitor()
180 itf
= new IntentFilter(Intent
.ACTION_BATTERY_CHANGED
);
181 batt_monitor
= new BroadcastReceiver()
184 public void onReceive(Context context
, Intent intent
)
186 /* we get literally spammed with battery statuses
187 * if we don't delay the re-attaching
189 TimerTask tk
= new TimerTask() {
191 registerReceiver(batt_monitor
, itf
);
194 Timer t
= new Timer();
195 context
.unregisterReceiver(this);
196 int rawlevel
= intent
.getIntExtra("level", -1);
197 int scale
= intent
.getIntExtra("scale", -1);
198 if (rawlevel
>= 0 && scale
> 0)
199 battery_level
= (rawlevel
* 100) / scale
;
202 /* query every 30s should be sufficient */
203 t
.schedule(tk
, 30000);
206 registerReceiver(batt_monitor
, itf
);
209 /* all below is heavily based on the examples found on
210 * http://developer.android.com/reference/android/app/Service.html
213 private void create_notification()
215 /* For now we'll use the same text for the ticker and the expanded notification */
216 CharSequence text
= getText(R
.string
.notification
);
217 /* Set the icon, scrolling text and timestamp */
218 notification
= new Notification(R
.drawable
.icon
, text
,
219 System
.currentTimeMillis());
221 /* The PendingIntent to launch our activity if the user selects this notification */
222 Intent intent
= new Intent(this, RockboxActivity
.class);
223 PendingIntent contentIntent
= PendingIntent
.getActivity(this, 0, intent
, 0);
225 /* Set the info for the views that show in the notification panel. */
226 notification
.setLatestEventInfo(this, getText(R
.string
.notification
), text
, contentIntent
);
229 public static void startForeground()
231 if (instance
!= null)
234 * Send the notification.
235 * We use a layout id because it is a unique number. We use it later to cancel.
237 instance
.mNM
.notify(R
.string
.notification
, instance
.notification
);
239 * this call makes the service run as foreground, which
240 * provides enough cpu time to do music decoding in the
243 instance
.startForegroundCompat(R
.string
.notification
, instance
.notification
);
247 public static void stopForeground()
249 if (instance
.notification
!= null)
251 instance
.stopForegroundCompat(R
.string
.notification
);
252 instance
.mNM
.cancel(R
.string
.notification
);
257 * This is a wrapper around the new startForeground method, using the older
258 * APIs if it is not available.
260 void startForegroundCompat(int id
, Notification notification
)
262 if (mStartForeground
!= null) {
263 mStartForegroundArgs
[0] = Integer
.valueOf(id
);
264 mStartForegroundArgs
[1] = notification
;
266 mStartForeground
.invoke(this, mStartForegroundArgs
);
267 } catch (InvocationTargetException e
) {
268 /* Should not happen. */
269 LOG("Unable to invoke startForeground", e
);
270 } catch (IllegalAccessException e
) {
271 /* Should not happen. */
272 LOG("Unable to invoke startForeground", e
);
277 /* Fall back on the old API.*/
279 mNM
.notify(id
, notification
);
283 * This is a wrapper around the new stopForeground method, using the older
284 * APIs if it is not available.
286 void stopForegroundCompat(int id
)
288 if (mStopForeground
!= null) {
289 mStopForegroundArgs
[0] = Boolean
.TRUE
;
291 mStopForeground
.invoke(this, mStopForegroundArgs
);
292 } catch (InvocationTargetException e
) {
293 /* Should not happen. */
294 LOG("Unable to invoke stopForeground", e
);
295 } catch (IllegalAccessException e
) {
296 /* Should not happen. */
297 LOG("Unable to invoke stopForeground", e
);
302 /* Fall back on the old API. Note to cancel BEFORE changing the
303 * foreground state, since we could be killed at that point. */
305 setForeground(false);
309 public void onDestroy() {
311 /* Make sure our notification is gone. */
312 stopForegroundCompat(R
.string
.notification
);