diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java index 4e5d9869..0aa03f81 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEvent.java @@ -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, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagementResult.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java similarity index 51% rename from app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagementResult.java rename to app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java index 9919fb55..fe5281f3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagementResult.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/deviceevents/GBDeviceEventAppManagement.java @@ -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, } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java index 71a1ad27..fb204e9e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleIoThread.java @@ -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())); } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index e2e6c241..017be70d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -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; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java index 518b50af..4c6ed136 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleSupport.java @@ -34,7 +34,7 @@ public class PebbleSupport extends AbstractBTDeviceSupport { @Override public void onInstallApp(Uri uri) { - getDeviceIOThread().installApp(uri); + getDeviceIOThread().installApp(uri, 0); } @Override