Skip to content

Commit 3d82510

Browse files
authored
Merge pull request #16 from matejdro/apps
App install
2 parents 0e9eb76 + 39bbc54 commit 3d82510

File tree

15 files changed

+716
-3
lines changed

15 files changed

+716
-3
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package io.rebble.libpebblecommon.disk
2+
3+
import io.rebble.libpebblecommon.packets.blobdb.AppMetadata
4+
import io.rebble.libpebblecommon.structmapper.*
5+
import io.rebble.libpebblecommon.util.DataBuffer
6+
7+
/**
8+
* Header of the
9+
*/
10+
class PbwBinHeader() : StructMappable() {
11+
/**
12+
* Major header version.
13+
*/
14+
val headerVersionMajor: SUByte = SUByte(m)
15+
16+
/**
17+
* Minor header version.
18+
*/
19+
val headerVersionMinor: SUByte = SUByte(m)
20+
21+
/**
22+
* Major sdk version.
23+
*/
24+
val sdkVersionMajor: SUByte = SUByte(m)
25+
26+
/**
27+
* Minor sdk version.
28+
*/
29+
val sdkVersionMinor: SUByte = SUByte(m)
30+
31+
/**
32+
* Major app version.
33+
*/
34+
val appVersionMajor: SUByte = SUByte(m)
35+
36+
/**
37+
* Minor app version.
38+
*/
39+
val appVersionMinor: SUByte = SUByte(m)
40+
41+
/**
42+
* Size of the app payload in bytes
43+
*/
44+
val appSize: SUShort = SUShort(m)
45+
46+
/**
47+
* ??? (Presumably offset where app payload starts?)
48+
*/
49+
val appOffset: SUInt = SUInt(m)
50+
51+
/**
52+
* CRC checksum of the app payload
53+
*/
54+
val crc: SUInt = SUInt(m)
55+
56+
/**
57+
* Name of the app
58+
*/
59+
val appName: SFixedString = SFixedString(m, 32)
60+
61+
/**
62+
* Name of the company that made the app
63+
*/
64+
val companyName: SFixedString = SFixedString(m, 32)
65+
66+
/**
67+
* Resource ID of the primary icon.
68+
*/
69+
val icon: SUInt = SUInt(m)
70+
71+
/**
72+
* ???
73+
*/
74+
val symbolTableAddress: SUInt = SUInt(m)
75+
76+
/**
77+
* List of app install flags. Should be forwarded to the watch when inserting into BlobDB.
78+
*/
79+
val flags: SUInt = SUInt(m)
80+
81+
/**
82+
* ???
83+
*/
84+
val numRelocationListEntries: SUInt = SUInt(m)
85+
86+
/**
87+
* UUID of the app
88+
*/
89+
val uuid: SUUID = SUUID(m)
90+
91+
fun toBlobDbApp(): AppMetadata {
92+
return AppMetadata().also {
93+
it.uuid.set(uuid.get())
94+
it.flags.set(flags.get())
95+
it.icon.set(icon.get())
96+
it.appVersionMajor.set(appVersionMajor.get())
97+
it.appVersionMinor.set(appVersionMinor.get())
98+
it.sdkVersionMajor.set(sdkVersionMajor.get())
99+
it.sdkVersionMinor.set(sdkVersionMinor.get())
100+
it.appName.set(appName.get())
101+
}
102+
}
103+
104+
companion object {
105+
const val SIZE: Int = 8 + 2 + 2 + 2 + 2 + 4 + 4 + 32 + 32 + 4 + 4 + 4 + 4 + 16
106+
107+
/**
108+
* Parse existing Pbw binary payload header. You should read [SIZE] bytes from the binary
109+
* payload and pass it into this method.
110+
*
111+
* @throws IllegalArgumentException if header is not valid pebble app header
112+
*/
113+
fun parseFileHeader(data: UByteArray): PbwBinHeader {
114+
if (data.size != SIZE) {
115+
throw IllegalArgumentException(
116+
"Read data from the file should be exactly $SIZE bytes"
117+
)
118+
}
119+
120+
val buffer = DataBuffer(data)
121+
122+
val sentinel = buffer.getBytes(8)
123+
if (!sentinel.contentEquals(EXPECTED_SENTINEL)) {
124+
throw IllegalArgumentException("Sentinel does not match")
125+
}
126+
127+
return PbwBinHeader().also {
128+
it.fromBytes(buffer)
129+
}
130+
}
131+
132+
/**
133+
* First 8 bytes of the header, spelling the word "PBLAPP" in ASCII,
134+
* followed by two zeros.
135+
*/
136+
private val EXPECTED_SENTINEL = ubyteArrayOf(
137+
0x50u, 0x42u, 0x4Cu, 0x41u, 0x50u, 0x50u, 0x00u, 0x00u
138+
)
139+
}
140+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.rebble.libpebblecommon.metadata
2+
3+
enum class WatchHardwarePlatform(val protocolNumber: UByte, val watchType: WatchType) {
4+
PEBBLE_ONE_EV_1(1u, WatchType.APLITE),
5+
PEBBLE_ONE_EV_2(2u, WatchType.APLITE),
6+
PEBBLE_ONE_EV_2_3(3u, WatchType.APLITE),
7+
PEBBLE_ONE_EV_2_4(4u, WatchType.APLITE),
8+
PEBBLE_ONE_POINT_FIVE(5u, WatchType.APLITE),
9+
PEBBLE_ONE_POINT_ZERO(6u, WatchType.APLITE),
10+
PEBBLE_SNOWY_EVT_2(7u, WatchType.BASALT),
11+
PEBBLE_SNOWY_DVT(8u, WatchType.BASALT),
12+
PEBBLE_BOBBY_SMILES(10u, WatchType.BASALT),
13+
PEBBLE_ONE_BIGBOARD_2(254u, WatchType.APLITE),
14+
PEBBLE_ONE_BIGBOARD(255u, WatchType.APLITE),
15+
PEBBLE_SNOWY_BIGBOARD(253u, WatchType.BASALT),
16+
PEBBLE_SNOWY_BIGBOARD_2(252u, WatchType.BASALT),
17+
PEBBLE_SPALDING_EVT(9u, WatchType.CHALK),
18+
PEBBLE_SPALDING_PVT(11u, WatchType.CHALK),
19+
PEBBLE_SPALDING_BIGBOARD(251u, WatchType.CHALK),
20+
PEBBLE_SILK_EVT(12u, WatchType.DIORITE),
21+
PEBBLE_SILK(14u, WatchType.DIORITE),
22+
PEBBLE_SILK_BIGBOARD(250u, WatchType.DIORITE),
23+
PEBBLE_SILK_BIGBOARD_2_PLUS(248u, WatchType.DIORITE),
24+
PEBBLE_ROBERT_EVT(13u, WatchType.EMERY),
25+
PEBBLE_ROBERT_BIGBOARD(249u, WatchType.EMERY),
26+
PEBBLE_ROBERT_BIGBOARD_2(247u, WatchType.EMERY);
27+
28+
companion object {
29+
fun fromProtocolNumber(number: UByte): WatchHardwarePlatform? {
30+
return values().firstOrNull { it.protocolNumber == number }
31+
}
32+
}
33+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package io.rebble.libpebblecommon.metadata
2+
3+
enum class WatchType(val codename: String) {
4+
APLITE("aplite"),
5+
BASALT("basalt"),
6+
CHALK("chalk"),
7+
DIORITE("diorite"),
8+
EMERY("emery")
9+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package io.rebble.libpebblecommon.packets
2+
3+
import io.rebble.libpebblecommon.protocolhelpers.PacketRegistry
4+
import io.rebble.libpebblecommon.protocolhelpers.PebblePacket
5+
import io.rebble.libpebblecommon.protocolhelpers.ProtocolEndpoint
6+
import io.rebble.libpebblecommon.structmapper.SUByte
7+
import io.rebble.libpebblecommon.structmapper.SUInt
8+
import io.rebble.libpebblecommon.structmapper.SUUID
9+
10+
sealed class AppFetchIncomingPacket() : PebblePacket(ProtocolEndpoint.APP_FETCH) {
11+
/**
12+
* Request command. See [AppFetchRequestCommand].
13+
*/
14+
val command = SUByte(m)
15+
16+
}
17+
18+
sealed class AppFetchOutgoingPacket(command: AppFetchRequestCommand) :
19+
PebblePacket(ProtocolEndpoint.APP_FETCH) {
20+
/**
21+
* Request command. See [AppFetchRequestCommand].
22+
*/
23+
val command = SUByte(m, command.value)
24+
25+
}
26+
27+
28+
/**
29+
* Packet sent from the watch when user opens an app that is not in the watch storage.
30+
*/
31+
class AppFetchRequest : AppFetchIncomingPacket() {
32+
33+
/**
34+
* UUID of the app to request
35+
*/
36+
val uuid = SUUID(m)
37+
38+
/**
39+
* ID of the app bank. Use in the [PutBytesAppInit] packet to identify this app install.
40+
*/
41+
val appId = SUInt(m, endianness = '<')
42+
}
43+
44+
/**
45+
* Packet sent from the watch when user opens an app that is not in the watch storage.
46+
*/
47+
class AppFetchResponse(
48+
status: AppFetchResponseStatus
49+
) : AppFetchOutgoingPacket(AppFetchRequestCommand.FETCH_APP) {
50+
/**
51+
* Response status
52+
*/
53+
val status = SUByte(m, status.value)
54+
55+
}
56+
57+
enum class AppFetchRequestCommand(val value: UByte) {
58+
FETCH_APP(0x01u)
59+
}
60+
61+
enum class AppFetchResponseStatus(val value: UByte) {
62+
/**
63+
* Sent right before starting to send PutBytes data
64+
*/
65+
START(0x01u),
66+
67+
/**
68+
* Sent when phone PutBytes is already busy sending something else
69+
*/
70+
BUSY(0x02u),
71+
72+
/**
73+
* Sent when UUID that watch sent is not in the locker
74+
*/
75+
INVALID_UUID(0x03u),
76+
77+
/**
78+
* Sent when there is generic data sending error (such as failure to read the local pbw file)
79+
*/
80+
NO_DATA(0x01u),
81+
}
82+
83+
84+
fun appFetchIncomingPacketsRegister() {
85+
PacketRegistry.register(
86+
ProtocolEndpoint.APP_FETCH,
87+
AppFetchRequestCommand.FETCH_APP.value
88+
) { AppFetchRequest() }
89+
}

0 commit comments

Comments
 (0)