Pebble: try to install app metadata on FW 3.x (untested)

live-activity-data
Andreas Shimokawa 2015-08-14 12:50:44 +02:00
parent 2e3de0cd0f
commit e28d6fa7cb
4 changed files with 108 additions and 15 deletions

View File

@ -70,7 +70,7 @@ public class PBWInstallHandler implements InstallHandler {
} }
GBDeviceApp app = mPBWReader.getGBDeviceApp(); GBDeviceApp app = mPBWReader.getGBDeviceApp();
File pbwFile = new File(mPBWReader.getUri().getPath()); File pbwFile = new File(mUri.getPath());
try { try {
File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache"); File destDir = new File(FileUtils.getExternalFilesDir() + "/pbw-cache");
destDir.mkdirs(); destDir.mkdirs();

View File

@ -14,6 +14,8 @@ import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -50,6 +52,10 @@ public class PBWReader {
private boolean isFirmware = false; private boolean isFirmware = false;
private boolean isValid = false; private boolean isValid = false;
private String hwRevision = null; private String hwRevision = null;
private short mSdkVersion;
private short mAppVersion;
private int mIconId;
private int mFlags;
public PBWReader(Uri uri, Context context, String platform) { public PBWReader(Uri uri, Context context, String platform) {
String platformDir = ""; String platformDir = "";
@ -70,7 +76,7 @@ public class PBWReader {
} }
ZipInputStream zis = new ZipInputStream(fin); ZipInputStream zis = new ZipInputStream(fin);
ZipEntry ze; ZipEntry ze;
pebbleInstallables = new ArrayList<PebbleInstallable>(); pebbleInstallables = new ArrayList<>();
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int count; int count;
try { try {
@ -149,6 +155,26 @@ public class PBWReader {
e.printStackTrace(); e.printStackTrace();
break; break;
} }
} else if (fileName.equals(platformDir + "pebble-app.bin")) {
zis.read(buffer, 0, 108);
byte[] tmp_buf = new byte[32];
ByteBuffer buf = ByteBuffer.wrap(buffer);
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.getLong(); // header, TODO: verifiy
buf.getShort(); // struct version, TODO: verify
mSdkVersion = buf.getShort();
mAppVersion = buf.getShort();
buf.getShort(); // size
buf.getInt(); // offset
buf.getInt(); // crc
buf.get(tmp_buf, 0, 32); // app name
buf.get(tmp_buf, 0, 32); // author
mIconId = buf.getInt();
LOG.info("got icon id from pebble-app.bin: " + mIconId);
buf.getInt(); // symbol table addr
mFlags = buf.getInt();
LOG.info("got flags from pebble-app.bin: " + mFlags);
// more follows but, not interesting for us
} }
} }
zis.close(); zis.close();
@ -206,7 +232,19 @@ public class PBWReader {
return hwRevision; return hwRevision;
} }
public Uri getUri() { public short getSdkVersion() {
return uri; return mSdkVersion;
}
public short getAppVersion() {
return mAppVersion;
}
public int getFlags() {
return mFlags;
}
public int getIconId() {
return mIconId;
} }
} }

View File

@ -26,6 +26,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagem
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PBWReader;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleInstallable;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; 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.GBDeviceIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.bt.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.service.bt.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
@ -42,8 +43,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
private boolean mIsConnected = false; private boolean mIsConnected = false;
private boolean mIsInstalling = false; private boolean mIsInstalling = false;
private int mConnectionAttempts = 0; private int mConnectionAttempts = 0;
/* app installation */ private boolean mForceUntested = false;
private Uri mInstallURI = null;
private PBWReader mPBWReader = null; private PBWReader mPBWReader = null;
private int mAppInstallToken = -1; private int mAppInstallToken = -1;
private ZipInputStream mZis = null; private ZipInputStream mZis = null;
@ -84,7 +84,7 @@ public class PebbleIoThread extends GBDeviceIoThread {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false)); mPebbleProtocol.setForceProtocol(sharedPrefs.getBoolean("pebble_force_protocol", false));
mForceUntested = sharedPrefs.getBoolean("pebble_force_untested", false);
gbDevice.setState(GBDevice.State.CONNECTED); gbDevice.setState(GBDevice.State.CONNECTED);
gbDevice.sendDeviceUpdateIntent(getContext()); gbDevice.sendDeviceUpdateIntent(getContext());
@ -375,9 +375,8 @@ public class PebbleIoThread extends GBDeviceIoThread {
return; return;
} }
mIsInstalling = true; mIsInstalling = true;
mInstallURI = uri;
mPBWReader = new PBWReader(mInstallURI, getContext(), gbDevice.getHardwareVersion().equals("dvt") ? "basalt" : "aplite"); mPBWReader = new PBWReader(uri, getContext(), gbDevice.getHardwareVersion().equals("dvt") ? "basalt" : "aplite");
mPebbleInstallables = mPBWReader.getPebbleInstallables(); mPebbleInstallables = mPBWReader.getPebbleInstallables();
mCurrentInstallableIndex = 0; mCurrentInstallableIndex = 0;
@ -399,8 +398,12 @@ public class PebbleIoThread extends GBDeviceIoThread {
mInstallSlot = 0; mInstallSlot = 0;
mInstallState = PebbleAppInstallState.START_INSTALL; mInstallState = PebbleAppInstallState.START_INSTALL;
} else { } else {
writeInstallApp(mPebbleProtocol.encodeAppDelete(mPBWReader.getGBDeviceApp().getUUID())); GBDeviceApp app = mPBWReader.getGBDeviceApp();
writeInstallApp(mPebbleProtocol.encodeAppDelete(app.getUUID()));
mInstallState = PebbleAppInstallState.WAIT_SLOT; mInstallState = PebbleAppInstallState.WAIT_SLOT;
if (mPebbleProtocol.isFw3x && mForceUntested) {
writeInstallApp(mPebbleProtocol.encodeInstallMetadata(app.getUUID(), app.getName(), mPBWReader.getAppVersion(), mPBWReader.getSdkVersion()));
}
} }
} }

View File

@ -61,6 +61,15 @@ public class PebbleProtocol extends GBDeviceProtocol {
static final byte APPRUNSTATE_START = 1; static final byte APPRUNSTATE_START = 1;
static final byte BLOBDB_INSERT = 1;
static final byte BLOBDB_DELETE = 4;
static final byte BLOBDB_CLEAR = 5;
static final byte BLOBDB_PIN = 1;
static final byte BLOBDB_APP = 2;
static final byte BLOBDB_REMINDER = 3;
static final byte BLOBDB_NOTIFICATION = 4;
static final byte NOTIFICATION_EMAIL = 0; static final byte NOTIFICATION_EMAIL = 0;
static final byte NOTIFICATION_SMS = 1; static final byte NOTIFICATION_SMS = 1;
static final byte NOTIFICATION_TWITTER = 2; static final byte NOTIFICATION_TWITTER = 2;
@ -179,6 +188,8 @@ public class PebbleProtocol extends GBDeviceProtocol {
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 byte LENGTH_UUID = 16;
private static final String[] hwRevisions = {"unknown", "ev1", "ev2", "ev2_3", "ev2_4", "v1_5", "v2_0", "evt2", "dvt"}; private static final String[] hwRevisions = {"unknown", "ev1", "ev2", "ev2_3", "ev2_4", "v1_5", "v2_0", "evt2", "dvt"};
private static Random mRandom = new Random(); private static Random mRandom = new Random();
@ -406,11 +417,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.order(ByteOrder.LITTLE_ENDIAN); buf.order(ByteOrder.LITTLE_ENDIAN);
// blobdb - 23 bytes // blobdb - 23 bytes
buf.put((byte) 0x01); // insert buf.put(BLOBDB_INSERT);
buf.putShort((short) mRandom.nextInt()); // token buf.putShort((short) mRandom.nextInt()); // token
buf.put((byte) 0x04); // db id (0x04 = notification) buf.put(BLOBDB_NOTIFICATION);
buf.put((byte) 16); // uuid length buf.put(LENGTH_UUID); // uuid length
byte[] uuid_buf = new byte[16]; byte[] uuid_buf = new byte[LENGTH_UUID];
mRandom.nextBytes(uuid_buf); mRandom.nextBytes(uuid_buf);
buf.put(uuid_buf); // random UUID buf.put(uuid_buf); // random UUID
buf.putShort(pin_length); // length of the encapsulated data buf.putShort(pin_length); // length of the encapsulated data
@ -458,6 +469,47 @@ public class PebbleProtocol extends GBDeviceProtocol {
return buf.array(); return buf.array();
} }
public byte[] encodeInstallMetadata(UUID uuid, String appName, short appVersion, short sdkVersion) {
// Calculate length first
final short BLOBDB_LENGTH = 23;
final short METADATA_LENGTH = 126;
final short length = (short) (BLOBDB_LENGTH + METADATA_LENGTH);
byte[] name_buf = new byte[96];
System.arraycopy(appName.getBytes(), 0, name_buf, 0, appName.length());
ByteBuffer buf = ByteBuffer.allocate(length + LENGTH_PREFIX);
// Encode Prefix
buf.order(ByteOrder.BIG_ENDIAN);
buf.putShort(length);
buf.putShort(ENDPOINT_BLOBDB);
buf.order(ByteOrder.LITTLE_ENDIAN);
// blobdb - 23 bytes
buf.put(BLOBDB_INSERT); // insert
buf.putShort((short) mRandom.nextInt()); // token
buf.put(BLOBDB_APP);
buf.put(LENGTH_UUID);
buf.order(ByteOrder.BIG_ENDIAN);
buf.putLong(uuid.getMostSignificantBits()); // watchapp uuid
buf.putLong(uuid.getLeastSignificantBits());
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putShort(METADATA_LENGTH); // length of the encapsulated data
buf.order(ByteOrder.BIG_ENDIAN);
buf.putLong(uuid.getMostSignificantBits()); // watchapp uuid
buf.putLong(uuid.getLeastSignificantBits());
buf.order(ByteOrder.LITTLE_ENDIAN);
buf.putInt(1); // icon_id
buf.putShort(appVersion);
buf.putShort(sdkVersion);
buf.put((byte) 0); // app_face_bgcolor
buf.put((byte) 0); // app_face_template_id
buf.put(name_buf); // 96 bytes
return buf.array();
}
public byte[] encodeGetTime() { public byte[] encodeGetTime() {
return encodeSimpleMessage(ENDPOINT_TIME, TIME_GETTIME); return encodeSimpleMessage(ENDPOINT_TIME, TIME_GETTIME);
} }
@ -762,7 +814,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
} }
byte[] encodeApplicationMessagePush(short endpoint, UUID uuid, ArrayList<Pair<Integer, Object>> pairs) { byte[] encodeApplicationMessagePush(short endpoint, UUID uuid, ArrayList<Pair<Integer, Object>> pairs) {
int length = 16 + 3; // UUID + (PUSH + id + length of dict) int length = LENGTH_UUID + 3; // UUID + (PUSH + id + length of dict)
for (Pair<Integer, Object> pair : pairs) { for (Pair<Integer, Object> pair : pairs) {
length += 7; // key + type + length length += 7; // key + type + length
if (pair.second instanceof Integer) { if (pair.second instanceof Integer) {