Fossil Hybrid: added unauthenticated, limited mode

This commit is contained in:
Daniel Dakhno
2020-11-12 03:49:52 +01:00
parent 9fafe3929f
commit 39ed94de87
7 changed files with 143 additions and 80 deletions

View File

@@ -527,13 +527,13 @@ public class FossilWatchAdapter extends WatchAdapter {
} catch (RuntimeException e) {
if (characteristic.getUuid().toString().equals("3dda0005-957f-7d4a-34a6-74696673696d")) {
GB.log("authentication failed", GB.ERROR, null);
setDeviceState(GBDevice.State.AUTHENTICATION_REQUIRED);
requestQueue.clear();
}
// setDeviceState(GBDevice.State.AUTHENTICATION_REQUIRED);
}else {
GB.log("error", GB.ERROR, e);
getDeviceSupport().notifiyException(fossilRequest.getName(), e);
GB.toast(fossilRequest.getName() + " failed", Toast.LENGTH_SHORT, GB.ERROR);
}
requestFinished = true;
}

View File

@@ -66,6 +66,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.parser.ActivityEntry;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.parser.ActivityFileParser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.RequestMtuRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.button.ButtonConfigurationGetRequest;
@@ -85,6 +87,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.AssetFilePutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedGetRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRawRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedInterface;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FirmwareFilePutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImageFactory;
@@ -105,6 +108,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.widget.WidgetsPutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.AnimationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.FactoryResetRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.SetTimeRequest;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@@ -139,18 +143,16 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
HashMap<String, Bitmap> appIconCache = new HashMap<>();
String lastPostedApp = null;
@Override
public void initialize() {
try {
getSecretKey();
} catch (IllegalAccessException e) {
GB.toast("erro getting key: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
new TransactionBuilder("init fail")
.add(new SetDeviceStateAction(getDeviceSupport().getDevice(), GBDevice.State.AUTHENTICATION_REQUIRED, getContext()))
.queue(getDeviceSupport().getQueue());
return;
enum CONNECTION_MODE {
NOT_INITIALIZED,
AUTHENTICATED,
NOT_AUTHENTICATED
}
CONNECTION_MODE connectionMode = CONNECTION_MODE.NOT_INITIALIZED;
@Override
public void initialize() {
saveRawActivityFiles = getDeviceSpecificPreferences().getBoolean("save_raw_activity_files", false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -162,23 +164,33 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
protected void initializeWithSupportedFileVersions() {
if (getDeviceSupport().getDevice().getFirmwareVersion().contains("prod")) {
GB.toast("Dummy FW, skipping initialization", Toast.LENGTH_LONG, GB.INFO);
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED), false);
return;
}
queueWrite(new SetDeviceStateRequest(GBDevice.State.AUTHENTICATING));
negotiateSymmetricKey();
}
private void initializeAfterAuthentication(boolean authenticated){
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZING));
if(!authenticated) GB.toast("Authentication failed, limited functionality", Toast.LENGTH_LONG, GB.ERROR);
loadNotificationConfigurations();
queueWrite(new NotificationFilterPutHRRequest(this.notificationConfigurations, this));
if(authenticated){
setVibrationStrength();
syncSettings();
setTime();
}
overwriteButtons(null);
loadBackground();
loadWidgets();
// renderWidgets();
@@ -187,6 +199,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED));
}
private void handleAuthenticationResult(boolean success){
if(this.connectionMode != CONNECTION_MODE.NOT_INITIALIZED) return;
this.connectionMode = success ? CONNECTION_MODE.AUTHENTICATED : CONNECTION_MODE.NOT_AUTHENTICATED;
this.initializeAfterAuthentication(success);
}
private void setVibrationStrength() {
Prefs prefs = new Prefs(getDeviceSpecificPreferences());
int vibrationStrengh = prefs.getInt(DeviceSettingsPreferenceConst.PREF_VIBRATION_STRENGH_PERCENTAGE, 2);
@@ -198,8 +216,14 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
public void setVibrationStrength(short strength) {
negotiateSymmetricKey();
queueWrite(new ConfigurationPutRequest(new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.VibrationStrengthConfigItem((byte) strength), this));
if(connectionMode == CONNECTION_MODE.NOT_AUTHENTICATED){
GB.toast("not available in unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR);
return;
}
queueWrite(
(FileEncryptedInterface) new ConfigurationPutRequest(new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.VibrationStrengthConfigItem((byte) strength), this)
);
}
private void loadNotificationConfigurations() {
@@ -337,7 +361,6 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
private void uploadWidgets() {
negotiateSymmetricKey();
ArrayList<Widget> systemWidgets = new ArrayList<>(widgets.size());
for (Widget widget : this.widgets) {
if (!(widget instanceof CustomWidget) && !widget.getWidgetType().isCustom())
@@ -559,8 +582,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
public void downloadFile(final FileHandle handle, boolean fileIsEncrypted) {
if (fileIsEncrypted) {
negotiateSymmetricKey();
queueWrite(new FileEncryptedGetRequest(handle, this) {
queueWrite((FileEncryptedInterface) new FileEncryptedGetRequest(handle, this) {
@Override
public void handleFileData(byte[] fileData) {
logger.debug("downloaded encrypted file");
@@ -589,17 +611,39 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
if (renderOnWatch && update) renderWidgets();
}
private void queueWrite(final FileEncryptedInterface request){
try {
queueWrite(new VerifyPrivateKeyRequest(
this.getSecretKey(),
this
){
@Override
protected void handleAuthenticationResult(boolean success) {
if(success){
GB.log("success auth", GB.INFO, null);
queueWrite((FossilRequest) request, true);
}
}
});
} catch (IllegalAccessException e) {
GB.toast("error getting key: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
}
}
private void negotiateSymmetricKey() {
try {
queueWrite(new VerifyPrivateKeyRequest(
this.getSecretKey(),
this
));
){
@Override
protected void handleAuthenticationResult(boolean success) {
FossilHRWatchAdapter.this.handleAuthenticationResult(success);
}
});
} catch (IllegalAccessException e) {
GB.toast("error getting key: " + e.getMessage(), Toast.LENGTH_LONG, GB.ERROR, e);
getDeviceSupport().getDevice().setState(GBDevice.State.AUTHENTICATION_REQUIRED);
getDeviceSupport().getDevice().sendDeviceUpdateIntent(getContext());
getDeviceSupport().getQueue().clear();
this.handleAuthenticationResult(false);
}
}
@@ -614,10 +658,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
public void setTime() {
negotiateSymmetricKey();
if(connectionMode == CONNECTION_MODE.NOT_AUTHENTICATED){
GB.toast("not available in unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR);
return;
}
queueWrite(
new ConfigurationPutRequest(this.generateTimeConfigItemNow() ,this), false
(FileEncryptedInterface) new ConfigurationPutRequest(this.generateTimeConfigItemNow(), this)
);
}
@@ -659,13 +705,17 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
public void onFetchActivityData() {
if(connectionMode == CONNECTION_MODE.NOT_AUTHENTICATED){
GB.toast("not available in unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR);
return;
}
syncSettings();
negotiateSymmetricKey();
queueWrite(new FileLookupRequest(FileHandle.ACTIVITY_FILE, this) {
@Override
public void handleFileLookup(final short fileHandle) {
queueWrite(new FileEncryptedGetRequest(fileHandle, FossilHRWatchAdapter.this) {
queueWrite((FileEncryptedInterface) new FileEncryptedGetRequest(fileHandle, FossilHRWatchAdapter.this) {
@Override
public void handleFileData(byte[] fileData) {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
@@ -725,9 +775,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
private void syncSettings() {
negotiateSymmetricKey();
if(connectionMode == CONNECTION_MODE.NOT_AUTHENTICATED){
GB.toast("not available in unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR);
return;
}
queueWrite(new ConfigurationGetRequest(this));
queueWrite((FileEncryptedInterface) new ConfigurationGetRequest(this));
}
@Override
@@ -1055,7 +1108,8 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
if (matcher.find()) {
firmware = matcher.group(0);
Version version = new Version(firmware);
if(version.compareTo(new Version("1.0.2.19")) == -1) singlePressEvent = "single_click";
if (version.compareTo(new Version("1.0.2.19")) == -1)
singlePressEvent = "single_click";
}
ButtonConfiguration[] buttonConfigurations = new ButtonConfiguration[]{

View File

@@ -84,6 +84,8 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
} else if (value[1] == 2) {
ResultCode code = ResultCode.fromCode(value[2]);
handleAuthenticationResult(code.inidicatesSuccess());
if (!code.inidicatesSuccess()) throw new RuntimeException("Authentication error: " + code + " (" + value[2] + ")");
@@ -91,6 +93,8 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
}
}
protected void handleAuthenticationResult(boolean success){}
@Override
public boolean isFinished() {
return isFinished;

View File

@@ -10,10 +10,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedInterface;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedLookupAndGetRequest;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ConfigurationGetRequest extends FileEncryptedLookupAndGetRequest {
public class ConfigurationGetRequest extends FileEncryptedLookupAndGetRequest implements FileEncryptedInterface {
public ConfigurationGetRequest(FossilHRWatchAdapter adapter) {
super(FileHandle.CONFIGURATION, adapter);
}

View File

@@ -42,7 +42,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public abstract class FileEncryptedGetRequest extends FossilRequest {
public abstract class FileEncryptedGetRequest extends FossilRequest implements FileEncryptedInterface{
private short handle;
private FossilHRWatchAdapter adapter;

View File

@@ -0,0 +1,4 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file;
public interface FileEncryptedInterface {
}

View File

@@ -36,7 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FileEncryptedPutRequest extends FossilRequest {
public class FileEncryptedPutRequest extends FossilRequest implements FileEncryptedInterface {
public enum UploadState {INITIALIZED, UPLOADING, CLOSING, UPLOADED}
public UploadState state;