mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Pebble: Firmware upgrade support
This commit is contained in:
parent
235c603d92
commit
7c60e4b595
@ -1,9 +1,11 @@
|
|||||||
###Changelog
|
###Changelog
|
||||||
|
|
||||||
####Next Release (probably 0.3.0)
|
####Next Release (probably 0.3.0)
|
||||||
* Fix installation problems with certain .pbw files
|
* Pebble: Firmware installation (USE AT YOUR OWN RISK)
|
||||||
* Volume control for Pebble
|
* Pebble: Fix installation problems with certain .pbw files
|
||||||
|
* Pebble: Volume control
|
||||||
* Add icon for activity tracker apps (icon by xphnx)
|
* Add icon for activity tracker apps (icon by xphnx)
|
||||||
|
* Let the application quit when in reconnecting state
|
||||||
|
|
||||||
####Version 0.2.0
|
####Version 0.2.0
|
||||||
* Experimental pbw installation support (watchfaces/apps)
|
* Experimental pbw installation support (watchfaces/apps)
|
||||||
|
@ -18,7 +18,8 @@ Features:
|
|||||||
* Apollo playback info (artist, album, track)
|
* Apollo playback info (artist, album, track)
|
||||||
* Music control: play/pause, next track, previous track, volume up, volume down
|
* Music control: play/pause, next track, previous track, volume up, volume down
|
||||||
* List and remove installed apps/watchfaces
|
* List and remove installed apps/watchfaces
|
||||||
* Install .pbw files (EXPERMIENTAL)
|
* Install .pbw files
|
||||||
|
* Install firmware from .pbz files (EXPERIMENTAL)
|
||||||
|
|
||||||
How to use:
|
How to use:
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
<data android:host="*" />
|
<data android:host="*" />
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
<data android:pathPattern=".*\\.pbw" />
|
<data android:pathPattern=".*\\.pbw" />
|
||||||
|
<data android:pathPattern=".*\\.pbz" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
@ -21,11 +23,28 @@ import nodomain.freeyourgadget.gadgetbridge.GBDeviceApp;
|
|||||||
|
|
||||||
public class PBWReader {
|
public class PBWReader {
|
||||||
private static final String TAG = PebbleIoThread.class.getSimpleName();
|
private static final String TAG = PebbleIoThread.class.getSimpleName();
|
||||||
|
private static final HashMap<String, Byte> appFileTypesMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
appFileTypesMap = new HashMap<String, Byte>();
|
||||||
|
appFileTypesMap.put("application", PebbleProtocol.PUTBYTES_TYPE_BINARY);
|
||||||
|
appFileTypesMap.put("resources", PebbleProtocol.PUTBYTES_TYPE_RESOURCES);
|
||||||
|
appFileTypesMap.put("worker", PebbleProtocol.PUTBYTES_TYPE_WORKER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final HashMap<String, Byte> fwFileTypesMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
fwFileTypesMap = new HashMap<String, Byte>();
|
||||||
|
fwFileTypesMap.put("firmware", PebbleProtocol.PUTBYTES_TYPE_FIRMWARE);
|
||||||
|
fwFileTypesMap.put("resources", PebbleProtocol.PUTBYTES_TYPE_SYSRESOURCES);
|
||||||
|
}
|
||||||
|
|
||||||
private GBDeviceApp app;
|
|
||||||
private final Uri uri;
|
private final Uri uri;
|
||||||
private final ContentResolver cr;
|
private final ContentResolver cr;
|
||||||
|
private GBDeviceApp app;
|
||||||
private ArrayList<PebbleInstallable> pebbleInstallables;
|
private ArrayList<PebbleInstallable> pebbleInstallables;
|
||||||
|
private boolean isFirmware = false;
|
||||||
|
|
||||||
public PBWReader(Uri uri, Context context) {
|
public PBWReader(Uri uri, Context context) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
@ -60,35 +79,32 @@ public class PBWReader {
|
|||||||
String jsonString = baos.toString();
|
String jsonString = baos.toString();
|
||||||
try {
|
try {
|
||||||
JSONObject json = new JSONObject(jsonString);
|
JSONObject json = new JSONObject(jsonString);
|
||||||
JSONObject application = json.getJSONObject("application");
|
String[] searchJSON;
|
||||||
|
HashMap<String, Byte> fileTypeMap;
|
||||||
|
|
||||||
String name = application.getString("name");
|
|
||||||
int size = application.getInt("size");
|
|
||||||
long crc = application.getLong("crc");
|
|
||||||
pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_BINARY));
|
|
||||||
Log.i(TAG, "found app binary to install: " + name);
|
|
||||||
try {
|
try {
|
||||||
JSONObject resources = json.getJSONObject("resources");
|
json.getJSONObject("firmware");
|
||||||
name = resources.getString("name");
|
fileTypeMap = fwFileTypesMap;
|
||||||
size = resources.getInt("size");
|
isFirmware = true;
|
||||||
crc = resources.getLong("crc");
|
|
||||||
pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_RESOURCES));
|
|
||||||
Log.i(TAG, "found resources to install: " + name);
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
// no resources, that is no problem
|
fileTypeMap = appFileTypesMap;
|
||||||
|
isFirmware = false;
|
||||||
}
|
}
|
||||||
try {
|
for (Map.Entry<String, Byte> entry : fileTypeMap.entrySet()) {
|
||||||
JSONObject worker = json.getJSONObject("worker");
|
try {
|
||||||
name = worker.getString("name");
|
JSONObject jo = json.getJSONObject(entry.getKey());
|
||||||
size = worker.getInt("size");
|
String name = jo.getString("name");
|
||||||
crc = worker.getLong("crc");
|
int size = jo.getInt("size");
|
||||||
pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, PebbleProtocol.PUTBYTES_TYPE_WORKER));
|
long crc = jo.getLong("crc");
|
||||||
Log.i(TAG, "found worker to install: " + name);
|
byte type = entry.getValue();
|
||||||
} catch (JSONException e) {
|
pebbleInstallables.add(new PebbleInstallable(name, size, (int) crc, type));
|
||||||
// no worker, that is no problem
|
Log.i(TAG, "found file to install: " + name);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// not fatal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
// no application, that is a problem
|
// no JSON at all that is a problem
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -126,6 +142,10 @@ public class PBWReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isFirmware() {
|
||||||
|
return isFirmware;
|
||||||
|
}
|
||||||
|
|
||||||
public GBDeviceApp getGBDeviceApp() {
|
public GBDeviceApp getGBDeviceApp() {
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,13 @@ public class PebbleAppInstallerActivity extends Activity {
|
|||||||
PBWReader pbwReader = new PBWReader(uri, getApplicationContext());
|
PBWReader pbwReader = new PBWReader(uri, getApplicationContext());
|
||||||
GBDeviceApp app = pbwReader.getGBDeviceApp();
|
GBDeviceApp app = pbwReader.getGBDeviceApp();
|
||||||
|
|
||||||
if (pbwReader != null && app != null) {
|
if (pbwReader != null) {
|
||||||
debugTextView.setText("THIS IS HIGHLY EXPERIMENTAL PROCEED AT YOUR OWN RISK\n\n\n" + app.getName() + " Version " + app.getVersion() + " by " + app.getCreator() + "\n");
|
if (pbwReader.isFirmware()) {
|
||||||
|
debugTextView.setText("YOUR ARE TRYING TO INSTALL A FIRMWARE, PROCEED AT YOUR OWN RISK, MAKE SURE THIS FIRMWARE IS FOR YOUR PEBBLE REVISION, THERE ARE NO CHECKS.\n\n\n");
|
||||||
|
|
||||||
|
} else if (app != null) {
|
||||||
|
debugTextView.setText("You are about to install the following app:\n\n\n" + app.getName() + " Version " + app.getVersion() + " by " + app.getCreator() + "\n");
|
||||||
|
}
|
||||||
installButton.setEnabled(true);
|
installButton.setEnabled(true);
|
||||||
installButton.setOnClickListener(new View.OnClickListener() {
|
installButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -121,16 +121,21 @@ public class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case APP_START_INSTALL:
|
case APP_START_INSTALL:
|
||||||
Log.i(TAG, "start installing app binary");
|
|
||||||
if (mPBWReader == null) {
|
if (mPBWReader == null) {
|
||||||
mPBWReader = new PBWReader(mInstallURI, getContext());
|
mPBWReader = new PBWReader(mInstallURI, getContext());
|
||||||
mPebbleInstallables = mPBWReader.getPebbleInstallables();
|
mPebbleInstallables = mPBWReader.getPebbleInstallables();
|
||||||
mCurrentInstallableIndex = 0;
|
mCurrentInstallableIndex = 0;
|
||||||
|
if (mPBWReader.isFirmware()) {
|
||||||
|
writeInstallApp(mPebbleProtocol.encodeInstallFirmwareStart());
|
||||||
|
mInstallSlot = 0;
|
||||||
|
Log.i(TAG, "starting firmware installation");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Log.i(TAG, "start installing app binary");
|
||||||
PebbleInstallable pi = mPebbleInstallables[mCurrentInstallableIndex];
|
PebbleInstallable pi = mPebbleInstallables[mCurrentInstallableIndex];
|
||||||
mZis = mPBWReader.getInputStreamFile(pi.getFileName());
|
mZis = mPBWReader.getInputStreamFile(pi.getFileName());
|
||||||
mCRC = pi.getCRC();
|
mCRC = pi.getCRC();
|
||||||
int binarySize = pi.getFileSize(); // TODO: use for progrssbar
|
int binarySize = pi.getFileSize(); // TODO: use for progressbar
|
||||||
writeInstallApp(mPebbleProtocol.encodeUploadStart(pi.getType(), (byte) mInstallSlot, binarySize));
|
writeInstallApp(mPebbleProtocol.encodeUploadStart(pi.getType(), (byte) mInstallSlot, binarySize));
|
||||||
mInstallState = PebbleAppInstallState.APP_WAIT_TOKEN;
|
mInstallState = PebbleAppInstallState.APP_WAIT_TOKEN;
|
||||||
break;
|
break;
|
||||||
@ -174,7 +179,12 @@ public class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case APP_REFRESH:
|
case APP_REFRESH:
|
||||||
writeInstallApp(mPebbleProtocol.encodeAppRefresh(mInstallSlot));
|
if (mPBWReader.isFirmware()) {
|
||||||
|
writeInstallApp(mPebbleProtocol.encodeInstallFirmwareComplete());
|
||||||
|
finishInstall(false);
|
||||||
|
} else {
|
||||||
|
writeInstallApp(mPebbleProtocol.encodeAppRefresh(mInstallSlot));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -239,7 +249,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
|
|||||||
gbDevice.sendDeviceUpdateIntent(getContext());
|
gbDevice.sendDeviceUpdateIntent(getContext());
|
||||||
GB.updateNotification("connection lost, trying to reconnect", getContext());
|
GB.updateNotification("connection lost, trying to reconnect", getContext());
|
||||||
|
|
||||||
while (mConnectionAttempts++ < 10) {
|
while (mConnectionAttempts++ < 10 && !mQuit) {
|
||||||
Log.i(TAG, "Trying to reconnect (attempt " + mConnectionAttempts + ")");
|
Log.i(TAG, "Trying to reconnect (attempt " + mConnectionAttempts + ")");
|
||||||
mIsConnected = connect(gbDevice.getAddress());
|
mIsConnected = connect(gbDevice.getAddress());
|
||||||
if (mIsConnected)
|
if (mIsConnected)
|
||||||
|
@ -96,6 +96,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
static final byte PUTBYTES_TYPE_FILE = 6;
|
static final byte PUTBYTES_TYPE_FILE = 6;
|
||||||
public static final byte PUTBYTES_TYPE_WORKER = 7;
|
public static final byte PUTBYTES_TYPE_WORKER = 7;
|
||||||
|
|
||||||
|
private final byte SYSTEMMESSAGE_FIRMWARESTART = 1;
|
||||||
|
private final byte SYSTEMMESSAGE_FIRMWARECOMPLETE = 2;
|
||||||
|
private final byte SYSTEMMESSAGE_FIRMWAREFAIL = 3;
|
||||||
|
|
||||||
static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains
|
static final byte PHONEVERSION_APPVERSION_MAGIC = 2; // increase this if pebble complains
|
||||||
static final byte PHONEVERSION_APPVERSION_MAJOR = 2;
|
static final byte PHONEVERSION_APPVERSION_MAJOR = 2;
|
||||||
static final byte PHONEVERSION_APPVERSION_MINOR = 3;
|
static final byte PHONEVERSION_APPVERSION_MINOR = 3;
|
||||||
@ -132,6 +136,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
static final short LENGTH_UPLOADCOMMIT = 9;
|
static final short LENGTH_UPLOADCOMMIT = 9;
|
||||||
static final short LENGTH_UPLOADCOMPLETE = 5;
|
static final short LENGTH_UPLOADCOMPLETE = 5;
|
||||||
static final short LENGTH_UPLOADCANCEL = 5;
|
static final short LENGTH_UPLOADCANCEL = 5;
|
||||||
|
static final short LENGTH_SYSTEMMESSAGE = 2;
|
||||||
|
|
||||||
private static byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) {
|
private static byte[] encodeMessage(short endpoint, byte type, int cookie, String[] parts) {
|
||||||
// Calculate length first
|
// Calculate length first
|
||||||
@ -360,6 +365,30 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
return buf.array();
|
return buf.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] encodeSystemMessage(byte systemMessage) {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_SYSTEMMESSAGE);
|
||||||
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
buf.putShort(LENGTH_SYSTEMMESSAGE);
|
||||||
|
buf.putShort(ENDPOINT_SYSTEMMESSAGE);
|
||||||
|
buf.put((byte) 0);
|
||||||
|
buf.put(systemMessage);
|
||||||
|
return buf.array();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] encodeInstallFirmwareStart() {
|
||||||
|
return encodeSystemMessage(SYSTEMMESSAGE_FIRMWARESTART);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] encodeInstallFirmwareComplete() {
|
||||||
|
return encodeSystemMessage(SYSTEMMESSAGE_FIRMWARECOMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] encodeInstallFirmwareError() {
|
||||||
|
return encodeSystemMessage(SYSTEMMESSAGE_FIRMWAREFAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public byte[] encodeAppRefresh(int index) {
|
public byte[] encodeAppRefresh(int index) {
|
||||||
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REFRESHAPP);
|
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_REFRESHAPP);
|
||||||
buf.order(ByteOrder.BIG_ENDIAN);
|
buf.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
Loading…
Reference in New Issue
Block a user