1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-01 03:55:47 +02:00

Huawei: Instalattion of apps fixed. Correct bin files support

This commit is contained in:
Me7c7 2024-08-29 23:43:27 +03:00 committed by José Rebelo
parent 74b9298a6b
commit 4989334ff4
5 changed files with 91 additions and 122 deletions

View File

@ -1,65 +0,0 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
import java.nio.ByteBuffer;
import java.util.ArrayList;
public class HuaweiAppParser {
public static class AppFileEntry {
public String filename;
public String folder;
public String unknown;
public byte[] content;
}
private String packageName;
private final ArrayList<AppFileEntry> entries = new ArrayList<>();
public String getPackageName() {
return packageName;
}
public byte[] getEntryContent(String filename) {
for(AppFileEntry en: entries) {
if(en.filename.equals(filename)) {
return en.content;
}
}
return null;
}
private byte[] readData(ByteBuffer data) {
int len = data.getInt();
byte[] newPayload = new byte[len];
data.get(newPayload, 0, len);
return newPayload;
}
private String readString(ByteBuffer data) {
return new String(readData(data));
}
public void parseData(byte[] in) throws Exception {
ByteBuffer data = ByteBuffer.wrap(in);
byte magic = data.get();
if(magic != (byte)0xbe){
throw new Exception("Invalid magic");
}
this.packageName = readString(data);
while(data.remaining() > 0) {
int pos = data.position();
byte a = data.get();
if(a != 0x00)
break;
data.position(pos);
AppFileEntry ent = new AppFileEntry();
ent.filename = readString(data);
ent.folder = readString(data);
ent.unknown = readString(data);
ent.content = readData(data);
entries.add(ent);
}
}
}

View File

@ -0,0 +1,86 @@
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
public class HuaweiBinAppParser {
public static class AppFileEntry {
public String filename;
public String path;
public byte[] content;
}
private static final int signHeaderLen = 32;
private static final byte[] signMagic = "hw signed app ".getBytes();
private static final byte[] signVersion = "1000".getBytes();
private String packageName;
private final ArrayList<AppFileEntry> entries = new ArrayList<>();
public String getPackageName() {
return packageName;
}
public byte[] getEntryContent(String filename) {
for(AppFileEntry en: entries) {
if(en.filename.equals(filename)) {
return en.content;
}
}
return null;
}
private byte[] readData(ByteBuffer data) {
long len = data.getLong();
byte[] newPayload = new byte[(int)len];
data.get(newPayload, 0, (int)len);
return newPayload;
}
private String readString(ByteBuffer data) {
int len = data.getInt();
byte[] newPayload = new byte[len];
data.get(newPayload, 0, len);
return new String(newPayload, StandardCharsets.UTF_8);
}
private int getSignLen(byte[] in) {
byte[] signatureHeader = Arrays.copyOfRange(in, in.length - signHeaderLen, in.length);
ByteBuffer signatureHeaderBuf = ByteBuffer.wrap(signatureHeader);
byte[] magic = new byte[signMagic.length];
signatureHeaderBuf.get(magic, 0, signMagic.length);
if(!Arrays.equals(signMagic, magic))
return 0;
byte[] version = new byte[signVersion.length];
signatureHeaderBuf.get(version, 0, signVersion.length);
if(!Arrays.equals(signVersion, version))
return 0;
//NOTE: we need only signature size.
return signatureHeaderBuf.getInt();
}
public void parseData(byte[] in) throws Exception {
//NOTE: Binary app file signed. We should avoid to read this signature.
int signLen = getSignLen(in);
ByteBuffer data = ByteBuffer.wrap(in);
byte magic = data.get();
if(magic != (byte)0xbe){
throw new Exception("Invalid magic");
}
this.packageName = readString(data);
while(data.remaining() > signLen) {
AppFileEntry ent = new AppFileEntry();
ent.filename = readString(data);
ent.path = readString(data);
ent.content = readData(data);
entries.add(ent);
}
}
}

View File

@ -108,16 +108,9 @@ public class HuaweiInstallHandler implements InstallHandler {
LOG.debug("Initialized HuaweiInstallHandler"); LOG.debug("Initialized HuaweiInstallHandler");
} else if (helper.isAPP()) { } else if (helper.isAPP()) {
final HuaweiCoordinatorSupplier huaweiCoordinatorSupplier = (HuaweiCoordinatorSupplier) coordinator; final HuaweiAppManager.AppConfig config = helper.getAppConfig();
String screenWindow = String.format("%d*%d", huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().height, this.valid = true; //NOTE: nothing to verify for now
huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().width);
String screenShape = huaweiCoordinatorSupplier.getHuaweiCoordinator().getAppDeviceParams().screenShape;
HuaweiAppManager.AppConfig config = helper.getAppconfig();
this.valid = config.checkDistroFilters(screenShape, screenWindow);
installActivity.setInstallEnabled(true); installActivity.setInstallEnabled(true);

View File

@ -1,7 +1,5 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei; package nodomain.freeyourgadget.gadgetbridge.service.devices.huawei;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -26,7 +24,6 @@ public class HuaweiAppManager {
public String vendor; public String vendor;
public String version; public String version;
JSONObject distroFilters = null;
public AppConfig(String jsonStr) { public AppConfig(String jsonStr) {
@ -36,52 +33,11 @@ public class HuaweiAppManager {
this.vendor = config.getJSONObject("app").getString("vendor"); this.vendor = config.getJSONObject("app").getString("vendor");
this.version = config.getJSONObject("app").getJSONObject("version").getString("name"); this.version = config.getJSONObject("app").getJSONObject("version").getString("name");
parseDistroFilters(config);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error decode app config", e); LOG.error("Error decode app config", e);
} }
} }
private void parseDistroFilters(JSONObject config) {
try {
distroFilters = config.getJSONObject("module").getJSONObject("distroFilter");
} catch (Exception e) {
LOG.error("Error decode app config distroFilter", e);
}
}
private boolean isValInArray(JSONArray arr, String value) throws JSONException {
for (int i = 0; i < arr.length(); i++) {
if (arr.getString(i).equals(value))
return true;
}
return false;
}
public boolean checkDistroFilters(String screenShape, String screenWindow) {
if (distroFilters == null)
return false;
try {
boolean screenShapeSupported = false;
boolean screenWindowSupported = false;
if (distroFilters.has("screenShape")) {
JSONArray values = distroFilters.getJSONObject("screenShape").getJSONArray("value");
String policy = distroFilters.getJSONObject("screenShape").getString("policy");
screenShapeSupported = isValInArray(values, screenShape) && policy.equals("include");
}
if (distroFilters.has("screenWindow")) {
JSONArray values = distroFilters.getJSONObject("screenWindow").getJSONArray("value");
String policy = distroFilters.getJSONObject("screenWindow").getString("policy");
screenWindowSupported = isValInArray(values, screenWindow) && policy.equals("include");
}
return screenShapeSupported && screenWindowSupported;
} catch (Exception e) {
LOG.error("Error decode app config distroFilter", e);
return false;
}
}
} }
private final HuaweiSupportProvider support; private final HuaweiSupportProvider support;

View File

@ -31,7 +31,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiAppParser; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiBinAppParser;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload; import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FileUpload;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile; import nodomain.freeyourgadget.gadgetbridge.util.GBZipFile;
@ -71,7 +71,6 @@ public class HuaweiFwHelper {
private void parseFile() { private void parseFile() {
if(parseAsApp()) { if(parseAsApp()) {
assert appConfig.bundleName != null; assert appConfig.bundleName != null;
assert appConfig.distroFilters != null;
fileType = FileUpload.Filetype.app; fileType = FileUpload.Filetype.app;
} else if (parseAsWatchFace()) { } else if (parseAsWatchFace()) {
assert watchfaceDescription.screen != null; assert watchfaceDescription.screen != null;
@ -99,7 +98,7 @@ public class HuaweiFwHelper {
buffer.flush(); buffer.flush();
byte[] hap_data = buffer.toByteArray(); byte[] hap_data = buffer.toByteArray();
HuaweiAppParser app = new HuaweiAppParser(); HuaweiBinAppParser app = new HuaweiBinAppParser();
app.parseData(hap_data); app.parseData(hap_data);
byte[] config = app.getEntryContent("config.json"); byte[] config = app.getEntryContent("config.json");
@ -212,7 +211,7 @@ public class HuaweiFwHelper {
return watchfaceDescription; return watchfaceDescription;
} }
public HuaweiAppManager.AppConfig getAppconfig() { public HuaweiAppManager.AppConfig getAppConfig() {
return appConfig; return appConfig;
} }