2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.androidtv.internal.protocol.googletv;
15 import static org.openhab.binding.androidtv.internal.AndroidTVBindingConstants.*;
16 import static org.openhab.binding.androidtv.internal.protocol.googletv.GoogleTVConstants.*;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
21 * Contains static methods for constructing LEAP messages
23 * @author Ben Rosenblum - Initial contribution
26 public class GoogleTVRequest {
28 public static String encodeMessage(String message) {
29 StringBuilder reply = new StringBuilder();
30 char[] charArray = message.toCharArray();
31 for (int i = 0; i < charArray.length; i = i + 2) {
32 String st = "" + charArray[i] + "" + charArray[i + 1];
33 char ch = (char) Integer.parseInt(st, 16);
36 return reply.toString();
39 public static String decodeMessage(String message) {
40 StringBuilder sb = new StringBuilder();
41 char ch[] = message.toCharArray();
42 for (int i = 0; i < ch.length; i++) {
43 String hexString = Integer.toHexString(ch[i]);
44 if (hexString.length() % 2 > 0) {
52 public static String pinRequest(String pin) {
54 if (PIN_REQUEST.equals(pin)) {
55 return loginRequest(3);
57 // 080210c801c202 22 0a 20 0e066c3d1c3a6686edb6b2648ff25fcb3f0bf9cc81deeee9fad1a26073645e17
58 // 080210c801c202 22 0a 20 530bb7c7ba06069997285aff6e0106adfb19ab23c18a7422f5f643b35a6467b3
59 // -------------------------SHA HASH OF PIN
61 int length = pin.length() / 2;
62 String len1 = GoogleTVRequest.fixMessage(Integer.toHexString(length + 2));
63 String len2 = GoogleTVRequest.fixMessage(Integer.toHexString(length));
64 return "080210c801c202" + len1 + "0a" + len2 + pin;
68 public static String loginRequest(int messageId) {
69 return loginRequest(messageId, "");
72 public static String loginRequest(int messageId, String data) {
75 // Send app and device name
76 // 080210c801522d 0a 19 636f6d2e676f6f676c652e616e64726f69642e766964656f73 12 10
77 // 73616d73756e6720534d2d4739393855
78 // ------------------LEN com.google.android.videos----------------------------LEN samsung SM-G998U
79 message = "080210c801522d0a19636f6d2e676f6f676c652e616e64726f69642e766964656f73121073616d73756e6720534d2d4739393855";
80 } else if (messageId == 2) {
81 // Unknown but required
82 // 080210c801a201 0e 0a 04 08031006 0a 04 08031004 1802
83 // ---------------LEN---LEN------------LEN
84 message = "080210c801a2010e0a04080310060a04080310041802";
85 } else if (messageId == 3) {
87 // ---------------LEN---LEN
88 // 080210c801a201 08 12 04 08031006 1801
89 // 080210c801f201 08 0a 04 08031006 1001
90 message = "080210c801f201080a04080310061001";
91 } else if (messageId == 4) {
92 // 0a 41 08 7e 12 3d 0a 08 534d2d4739393855 12 07 73616d73756e67 18 01 22 02 3133 2a
93 // ---LEN---------LEN---LEN--SM-G998U----------LEN--samsung---------
94 // 19 636f6d2e676f6f676c652e616e64726f69642e766964656f73 32
95 // LEN-com.google.android.videos----------------------------
99 // "0a41087e123d0a08534d2d4739393855120773616d73756e671801220231332a19636f6d2e676f6f676c652e616e64726f69642e766964656f733207342e33382e3138";
101 // 0a 57 08 fe 04 12 52 0a 08 534d2d4739393855 12 07 73616d73756e67 18 01 22 02 3133 2a
102 // ---LEN------------LEN---LEN--SM-G998U----------LEN--samsung---------
103 // 19 636f6d2e676f6f676c652e616e64726f69642e766964656f73 32
104 // LEN-com.google.android.videos---------------------------
105 // 1c 342e33392e3538342e3532393538383538332e372d72656c65617365
106 // LEN-4.39.584.529588583.7-release
108 // "0a5708fe0412520a08534d2d4739393855120773616d73756e671801220231332a19636f6d2e676f6f676c652e616e64726f69642e766964656f73321c342e33392e3538342e3532393538383538332e372d72656c65617365";
110 // 0a 3b 08 ee 04 12 36 0a 06 6950686f6e65 12 05 4170706c65 18 02 22 04 31362e36 2a
111 // ---LEN------------LEN---LEN-iPhone---------LEN-Apple--------------LEN-16.6
112 // 11 636f6d2e676f6f676c652e4d6f76696573 32 0a 332e31332e3030303033
113 // LEN-com.google.Movies--------------------LEN-3.13.00003
115 // 0a 57 08 fe 04 12 52 0a 08 534d2d4739393855 12 07 73616d73756e67 18 01 22 02 3133 2a
116 // ---LEN------------LEN---LEN--SM-G998U----------LEN--samsung---------
117 // 19 636f6d2e676f-6f676c652e616e64726f69642e766964656f73 32
118 // LEN-com.google.android.videos---------------------------
119 // 1c 342e33392e3634342e3533343836353739392e332d72656c65617365
120 // LEN-4.39.644.534865799.3-release
121 int dataInt = Integer.parseInt(data, 16) - 1;
122 String updatedData = Integer.toHexString(dataInt);
123 message = "0a5708" + updatedData
124 + "0412520a08534d2d4739393855120773616d73756e671801220231332a19636f6d2e676f6f676c652e616e64726f69642e766964656f73321c342e33392e3634342e3533343836353739392e332d72656c65617365";
125 } else if (messageId == 5) {
126 // Unknown. Sent after "1200" received
127 message = "1202087e";
132 public static String keepAlive(String request) {
133 // 42 07 08 01 10 e4f1 8d01
136 // 42 08 08 7f 10 b4 908a a819
139 // 42 09 08 8001 10 ed b78a a819
142 char[] charArray = request.toCharArray();
143 StringBuilder sb = new StringBuilder();
145 sb.setLength(sb.toString().length() - 6);
148 int sbLen = sb.toString().length();
149 st = "" + charArray[sbLen - 2] + charArray[sbLen - 1];
150 if (!DELIMITER_10.equals(st)) {
151 sb.setLength(sbLen - 2);
153 } while (!DELIMITER_10.equals(st));
154 sb.setLength(sb.toString().length() - 2);
156 StringBuilder sbReply = new StringBuilder();
157 for (int i = 4; i < sb.toString().length(); i++) {
158 sbReply.append(charArray[i]);
160 return "4a" + fixMessage(Integer.toHexString(sbReply.toString().length() / 2)) + sbReply.toString();
163 public static String fixMessage(String tempMsg) {
164 if (tempMsg.length() % 2 > 0) {
165 tempMsg = "0" + tempMsg;