Pebble: blindly implement missing parts for app installation on FW 3.x

This is expected to fail and needs to be enable in preferences (untested features).
This commit is contained in:
Andreas Shimokawa 2015-08-16 00:32:36 +02:00
parent 567f27b0f4
commit a4f5524f6e
5 changed files with 84 additions and 40 deletions

View File

@ -10,7 +10,7 @@ public abstract class GBDeviceEvent {
CALL_CONTROL,
APP_INFO,
VERSION_INFO,
APP_MANAGEMENT_RES,
APP_MANAGEMENT,
SEND_BYTES,
SLEEP_MONITOR_RES,
SCREENSHOT,

View File

@ -1,12 +1,15 @@
package nodomain.freeyourgadget.gadgetbridge.deviceevents;
public class GBDeviceEventAppManagementResult extends GBDeviceEvent {
public Result result = Result.UNKNOWN;
import java.util.UUID;
public class GBDeviceEventAppManagement extends GBDeviceEvent {
public Event event = Event.UNKNOWN;
public EventType type = EventType.UNKNOWN;
public int token = -1;
public UUID uuid = null;
public GBDeviceEventAppManagementResult() {
eventClass = EventClass.APP_MANAGEMENT_RES;
public GBDeviceEventAppManagement() {
eventClass = EventClass.APP_MANAGEMENT;
}
public enum EventType {
@ -15,10 +18,11 @@ public class GBDeviceEventAppManagementResult extends GBDeviceEvent {
DELETE,
}
public enum Result {
public enum Event {
UNKNOWN,
SUCCESS,
ACKNOLEDGE,
FAILURE,
REQUEST,
}
}

View File

@ -12,6 +12,7 @@ import android.preference.PreferenceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -22,13 +23,14 @@ import java.util.zip.ZipInputStream;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagementResult;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.service.bt.GBDeviceIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.bt.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class PebbleIoThread extends GBDeviceIoThread {
@ -122,7 +124,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
mCRC = pi.getCRC();
mBinarySize = pi.getFileSize();
mBytesWritten = 0;
writeInstallApp(mPebbleProtocol.encodeUploadStart(pi.getType(), (byte) mInstallSlot, mBinarySize));
writeInstallApp(mPebbleProtocol.encodeUploadStart(pi.getType(), mInstallSlot, mBinarySize));
mInstallState = PebbleAppInstallState.WAIT_TOKEN;
break;
case WAIT_TOKEN:
@ -282,12 +284,12 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
gbDevice.setState(GBDevice.State.INITIALIZED);
return false;
case APP_MANAGEMENT_RES:
GBDeviceEventAppManagementResult appMgmtRes = (GBDeviceEventAppManagementResult) deviceEvent;
switch (appMgmtRes.type) {
case APP_MANAGEMENT:
GBDeviceEventAppManagement appMgmt = (GBDeviceEventAppManagement) deviceEvent;
switch (appMgmt.type) {
case DELETE:
// right now on the Pebble we also receive this on a failed/successful installation ;/
switch (appMgmtRes.result) {
switch (appMgmt.event) {
case FAILURE:
if (mIsInstalling) {
if (mInstallState == PebbleAppInstallState.WAIT_SLOT) {
@ -320,13 +322,21 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
break;
case INSTALL:
switch (appMgmtRes.result) {
switch (appMgmt.event) {
case FAILURE:
LOG.info("failure installing app"); // TODO: report to Installer
finishInstall(true);
break;
case SUCCESS:
setToken(appMgmtRes.token);
setToken(appMgmt.token);
break;
case REQUEST:
LOG.info("APPFETCH request: " + appMgmt.uuid + " / " + appMgmt.token);
try {
installApp(Uri.fromFile(new File(FileUtils.getExternalFilesDir() + "/pbw-cache/" + appMgmt.uuid.toString() + ".pbw")), appMgmt.token);
} catch (IOException e) {
e.printStackTrace();
}
break;
default:
break;
@ -369,18 +379,21 @@ public class PebbleIoThread extends GBDeviceIoThread {
}
}
public void installApp(Uri uri) {
public void installApp(Uri uri, int appId) {
if (mIsInstalling) {
return;
}
mIsInstalling = true;
mPBWReader = new PBWReader(uri, getContext(), gbDevice.getHardwareVersion().equals("dvt") ? "basalt" : "aplite");
mPebbleInstallables = mPBWReader.getPebbleInstallables();
mCurrentInstallableIndex = 0;
if (mPBWReader.isFirmware()) {
LOG.info("starting firmware installation");
mIsInstalling = true;
mInstallSlot = 0;
writeInstallApp(mPebbleProtocol.encodeInstallFirmwareStart());
mInstallState = PebbleAppInstallState.START_INSTALL;
/*
* This is a hack for recovery mode, in which the blocking read has no timeout and the
@ -392,16 +405,25 @@ public class PebbleIoThread extends GBDeviceIoThread {
*
*/
writeInstallApp(mPebbleProtocol.encodeGetTime());
LOG.info("starting firmware installation");
mInstallSlot = 0;
mInstallState = PebbleAppInstallState.START_INSTALL;
} else {
GBDeviceApp app = mPBWReader.getGBDeviceApp();
writeInstallApp(mPebbleProtocol.encodeAppDelete(app.getUUID()));
mInstallState = PebbleAppInstallState.WAIT_SLOT;
if (mPebbleProtocol.isFw3x && mForceUntested) {
writeInstallApp(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion()));
if (appId == 0) {
// only install metadata - not the binaries
write(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion()));
GB.toast("To finish installation please start the watchapp on your Pebble", 5, GB.INFO);
} else {
// this came from an app fetch request, so do the real stuff
mIsInstalling = true;
mInstallSlot = appId;
mInstallState = PebbleAppInstallState.START_INSTALL;
writeInstallApp(mPebbleProtocol.encodeGetTime()); // EVIL HACK see hack above
}
} else {
mIsInstalling = true;
mInstallState = PebbleAppInstallState.WAIT_SLOT;
writeInstallApp(mPebbleProtocol.encodeAppDelete(app.getUUID()));
}
}
}

View File

@ -15,7 +15,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagementResult;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDismissNotification;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
@ -182,7 +182,8 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final short LENGTH_REFRESHAPP = 5;
static final short LENGTH_SETTIME = 5;
static final short LENGTH_SYSTEMMESSAGE = 2;
static final short LENGTH_UPLOADSTART = 7;
static final short LENGTH_UPLOADSTART_2X = 7;
static final short LENGTH_UPLOADSTART_3X = 10;
static final short LENGTH_UPLOADCHUNK = 9;
static final short LENGTH_UPLOADCOMMIT = 9;
static final short LENGTH_UPLOADCOMPLETE = 5;
@ -658,15 +659,27 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
/* pebble specific install methods */
public byte[] encodeUploadStart(byte type, byte index, int size) {
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + LENGTH_UPLOADSTART);
public byte[] encodeUploadStart(byte type, int app_id, int size) {
short length;
if (isFw3x) {
length = LENGTH_UPLOADSTART_3X;
type |= 0b10000000;
} else {
length = LENGTH_UPLOADSTART_2X;
}
ByteBuffer buf = ByteBuffer.allocate(LENGTH_PREFIX + length);
buf.order(ByteOrder.BIG_ENDIAN);
buf.putShort(LENGTH_UPLOADSTART);
buf.putShort(length);
buf.putShort(ENDPOINT_PUTBYTES);
buf.put(PUTBYTES_INIT);
buf.putInt(size);
buf.put(type);
buf.put(index);
if (isFw3x) {
buf.putInt(app_id);
} else {
// slot
buf.put((byte) app_id);
}
return buf.array();
}
@ -940,7 +953,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
return null;
}
private GBDeviceEvent decodeAppFetch(ByteBuffer buf) {
private GBDeviceEventAppManagement decodeAppFetch(ByteBuffer buf) {
buf.order(ByteOrder.LITTLE_ENDIAN);
byte command = buf.get();
if (command == 0x01) {
@ -948,7 +961,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
long uuid_low = buf.getLong();
UUID uuid = new UUID(uuid_high, uuid_low);
int app_id = buf.getInt();
LOG.info("APPFETCH request: " + uuid + " / " + app_id);
GBDeviceEventAppManagement fetchRequest = new GBDeviceEventAppManagement();
fetchRequest.type = GBDeviceEventAppManagement.EventType.INSTALL;
fetchRequest.event = GBDeviceEventAppManagement.Event.REQUEST;
fetchRequest.token = app_id;
fetchRequest.uuid = uuid;
return fetchRequest;
}
return null;
}
@ -1080,16 +1098,16 @@ public class PebbleProtocol extends GBDeviceProtocol {
}
break;
case APPMANAGER_REMOVEAPP:
GBDeviceEventAppManagementResult deleteRes = new GBDeviceEventAppManagementResult();
deleteRes.type = GBDeviceEventAppManagementResult.EventType.DELETE;
GBDeviceEventAppManagement deleteRes = new GBDeviceEventAppManagement();
deleteRes.type = GBDeviceEventAppManagement.EventType.DELETE;
int result = buf.getInt();
switch (result) {
case APPMANAGER_RES_SUCCESS:
deleteRes.result = GBDeviceEventAppManagementResult.Result.SUCCESS;
deleteRes.event = GBDeviceEventAppManagement.Event.SUCCESS;
break;
default:
deleteRes.result = GBDeviceEventAppManagementResult.Result.FAILURE;
deleteRes.event = GBDeviceEventAppManagement.Event.FAILURE;
break;
}
devEvt = deleteRes;
@ -1101,16 +1119,16 @@ public class PebbleProtocol extends GBDeviceProtocol {
break;
case ENDPOINT_PUTBYTES:
pebbleCmd = buf.get();
GBDeviceEventAppManagementResult installRes = new GBDeviceEventAppManagementResult();
installRes.type = GBDeviceEventAppManagementResult.EventType.INSTALL;
GBDeviceEventAppManagement installRes = new GBDeviceEventAppManagement();
installRes.type = GBDeviceEventAppManagement.EventType.INSTALL;
switch (pebbleCmd) {
case PUTBYTES_INIT:
installRes.token = buf.getInt();
installRes.result = GBDeviceEventAppManagementResult.Result.SUCCESS;
installRes.event = GBDeviceEventAppManagement.Event.SUCCESS;
break;
default:
installRes.token = buf.getInt();
installRes.result = GBDeviceEventAppManagementResult.Result.FAILURE;
installRes.event = GBDeviceEventAppManagement.Event.FAILURE;
break;
}
devEvt = installRes;

View File

@ -34,7 +34,7 @@ public class PebbleSupport extends AbstractBTDeviceSupport {
@Override
public void onInstallApp(Uri uri) {
getDeviceIOThread().installApp(uri);
getDeviceIOThread().installApp(uri, 0);
}
@Override