1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2010 Thomas Martitz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
24 import java
.io
.BufferedInputStream
;
25 import java
.io
.BufferedOutputStream
;
27 import java
.io
.FileOutputStream
;
28 import java
.util
.Enumeration
;
29 import java
.util
.Timer
;
30 import java
.util
.TimerTask
;
31 import java
.util
.zip
.ZipEntry
;
32 import java
.util
.zip
.ZipFile
;
34 import org
.rockbox
.Helper
.RunForegroundManager
;
36 import android
.app
.Activity
;
37 import android
.app
.Service
;
38 import android
.content
.BroadcastReceiver
;
39 import android
.content
.Context
;
40 import android
.content
.Intent
;
41 import android
.content
.IntentFilter
;
42 import android
.os
.Bundle
;
43 import android
.os
.IBinder
;
44 import android
.os
.ResultReceiver
;
45 import android
.util
.Log
;
47 /* This class is used as the main glue between java and c.
48 * All access should be done through RockboxService.get_instance() for safety.
51 public class RockboxService
extends Service
53 /* this Service is really a singleton class - well almost.
54 * To do it properly this line should be instance = new RockboxService()
55 * but apparently that doesnt work with the way android Services are created.
57 private static RockboxService instance
= null;
59 /* locals needed for the c code and rockbox state */
60 private RockboxFramebuffer fb
= null;
61 private boolean mRockboxRunning
= false;
62 private volatile boolean rbLibLoaded
;
63 private Activity current_activity
= null;
64 private IntentFilter itf
;
65 private BroadcastReceiver batt_monitor
;
66 private RunForegroundManager fg_runner
;
67 @SuppressWarnings("unused")
68 private int battery_level
;
69 private ResultReceiver resultReceiver
;
71 public static final int RESULT_LIB_LOADED
= 0;
72 public static final int RESULT_LIB_LOAD_PROGRESS
= 1;
73 public static final int RESULT_FB_INITIALIZED
= 2;
74 public static final int RESULT_ERROR_OCCURED
= 3;
77 public void onCreate()
82 public static RockboxService
get_instance()
87 public RockboxFramebuffer
get_fb()
91 /* framebuffer is initialised by the native code(!) so this is needed */
92 public void set_fb(RockboxFramebuffer newfb
)
95 mRockboxRunning
= true;
96 if (resultReceiver
!= null)
97 resultReceiver
.send(RESULT_FB_INITIALIZED
, null);
100 public Activity
get_activity()
102 return current_activity
;
104 public void set_activity(Activity a
)
106 current_activity
= a
;
109 private void do_start(Intent intent
)
111 LOG("Start Service");
113 if (intent
!= null && intent
.hasExtra("callback"))
114 resultReceiver
= (ResultReceiver
) intent
.getParcelableExtra("callback");
118 /* Display a notification about us starting.
119 * We put an icon in the status bar. */
120 if (fg_runner
== null)
123 fg_runner
= new RunForegroundManager(this);
124 } catch (Exception e
) {
130 private void LOG(CharSequence text
)
132 Log
.d("Rockbox", (String
) text
);
135 private void LOG(CharSequence text
, Throwable tr
)
137 Log
.d("Rockbox", (String
) text
, tr
);
140 public void onStart(Intent intent
, int startId
) {
144 public int onStartCommand(Intent intent
, int flags
, int startId
)
147 return 1; /* old API compatibility: 1 == START_STICKY */
150 private void startservice()
152 final int BUFFER
= 8*1024;
153 Thread rb
= new Thread(new Runnable()
157 String rockboxDirPath
= "/data/data/org.rockbox/app_rockbox/rockbox";
158 File rockboxDir
= new File(rockboxDirPath
);
160 /* the following block unzips libmisc.so, which contains the files
161 * we ship, such as themes. It's needed to put it into a .so file
162 * because there's no other way to ship files and have access
163 * to them from native code
165 File libMisc
= new File("/data/data/org.rockbox/lib/libmisc.so");
166 /* use arbitrary file to determine whether extracting is needed */
167 File arbitraryFile
= new File(rockboxDir
, "viewers.config");
168 if (!arbitraryFile
.exists() || (libMisc
.lastModified() > arbitraryFile
.lastModified()))
172 Bundle progressData
= new Bundle();
173 byte data
[] = new byte[BUFFER
];
174 ZipFile zipfile
= new ZipFile(libMisc
);
175 Enumeration
<?
extends ZipEntry
> e
= zipfile
.entries();
176 progressData
.putInt("max", zipfile
.size());
178 while(e
.hasMoreElements())
180 ZipEntry entry
= (ZipEntry
) e
.nextElement();
182 /* strip off /.rockbox when extracting */
183 String fileName
= entry
.getName();
184 int slashIndex
= fileName
.indexOf('/', 1);
185 file
= new File(rockboxDirPath
+ fileName
.substring(slashIndex
));
187 if (!entry
.isDirectory())
189 /* Create the parent folders if necessary */
190 File folder
= new File(file
.getParent());
191 if (!folder
.exists())
195 BufferedInputStream is
= new BufferedInputStream(zipfile
.getInputStream(entry
), BUFFER
);
196 FileOutputStream fos
= new FileOutputStream(file
);
197 BufferedOutputStream dest
= new BufferedOutputStream(fos
, BUFFER
);
200 while ((count
= is
.read(data
, 0, BUFFER
)) != -1)
201 dest
.write(data
, 0, count
);
208 if (resultReceiver
!= null) {
209 progressData
.putInt("value", progressData
.getInt("value", 0) + 1);
210 resultReceiver
.send(RESULT_LIB_LOAD_PROGRESS
, progressData
);
213 } catch(Exception e
) {
214 LOG("Exception when unzipping", e
);
216 if (resultReceiver
!= null) {
217 Bundle bundle
= new Bundle();
218 bundle
.putString("error", getString(R
.string
.error_extraction
));
219 resultReceiver
.send(RESULT_ERROR_OCCURED
, bundle
);
224 System
.loadLibrary("rockbox");
226 if (resultReceiver
!= null)
227 resultReceiver
.send(RESULT_LIB_LOADED
, null);
230 throw new IllegalStateException("native main() returned!");
232 }, "Rockbox thread");
236 private native void main();
238 /* returns true once rockbox is up and running.
239 * This is considered done once the framebuffer is initialised
241 public boolean isRockboxRunning()
243 return mRockboxRunning
;
247 public IBinder
onBind(Intent intent
)
249 // TODO Auto-generated method stub
254 @SuppressWarnings("unused")
256 * Sets up the battery monitor which receives the battery level
257 * about each 30 seconds
259 private void initBatteryMonitor()
261 itf
= new IntentFilter(Intent
.ACTION_BATTERY_CHANGED
);
262 batt_monitor
= new BroadcastReceiver()
265 public void onReceive(Context context
, Intent intent
)
267 /* we get literally spammed with battery statuses
268 * if we don't delay the re-attaching
270 TimerTask tk
= new TimerTask()
274 registerReceiver(batt_monitor
, itf
);
277 Timer t
= new Timer();
278 context
.unregisterReceiver(this);
279 int rawlevel
= intent
.getIntExtra("level", -1);
280 int scale
= intent
.getIntExtra("scale", -1);
281 if (rawlevel
>= 0 && scale
> 0)
282 battery_level
= (rawlevel
* 100) / scale
;
285 /* query every 30s should be sufficient */
286 t
.schedule(tk
, 30000);
289 registerReceiver(batt_monitor
, itf
);
292 public void startForeground()
294 fg_runner
.startForeground();
297 public void stopForeground()
299 fg_runner
.stopForeground();
303 public void onDestroy()
307 /* Make sure our notification is gone. */