mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-11-28 06:03:00 +00:00
Various changes
- Extend Wear support - Rework Gms Services - Fix ProGuard - Add Waze to Google Whitelist (as in original Play Services, fixes #116)
This commit is contained in:
parent
62408d036c
commit
2a394f98aa
38 changed files with 801 additions and 637 deletions
2
extern/GmsApi
vendored
2
extern/GmsApi
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 75562ce8b6d19fccde3ca46ab853513f48832a05
|
Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335
|
2
extern/UnifiedNlp
vendored
2
extern/UnifiedNlp
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit d184e0e7f10aaad979019d3efe5fc10a68299849
|
Subproject commit 97b01d50d4fc54061ff8495e92217f31454acd0a
|
2
extern/Wearable
vendored
2
extern/Wearable
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit c12fe119c8f71b4e7b458f480270f686dae34343
|
Subproject commit 5c24adaa3928de68167ce31c0fa5b9b1a3256677
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.6-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip
|
||||||
|
|
|
@ -19,7 +19,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:1.5.0'
|
classpath 'com.android.tools.build:gradle:2.0.0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,11 +56,11 @@ dependencies {
|
||||||
|
|
||||||
String getMyVersionName() {
|
String getMyVersionName() {
|
||||||
def stdout = new ByteArrayOutputStream()
|
def stdout = new ByteArrayOutputStream()
|
||||||
exec {
|
if (rootProject.file("gradlew").exists())
|
||||||
commandLine 'git', 'describe', '--tags', '--always', '--dirty'
|
exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
|
||||||
standardOutput = stdout
|
else // automatic build system, don't tag dirty
|
||||||
}
|
exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
|
||||||
return stdout.toString().trim()
|
return stdout.toString().trim().substring(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
int getMyVersionCode(String ref) {
|
int getMyVersionCode(String ref) {
|
||||||
|
|
|
@ -79,7 +79,6 @@
|
||||||
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
|
||||||
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
|
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
|
||||||
<uses-permission android:name="org.microg.gms.STATUS_BROADCAST"/>
|
<uses-permission android:name="org.microg.gms.STATUS_BROADCAST"/>
|
||||||
<uses-permission android:name="org.microg.gms.EXTENDED_ACCESS"/>
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
|
|
@ -29,30 +29,16 @@ import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
||||||
import com.google.android.gms.common.internal.ValidateAccountRequest;
|
import com.google.android.gms.common.internal.ValidateAccountRequest;
|
||||||
|
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
public static final int ID_ACCEPT_ALL = -1;
|
|
||||||
|
|
||||||
private static final String TAG = "GmsServiceBroker";
|
private static final String TAG = "GmsServiceBroker";
|
||||||
private final Set<Integer> supportedServiceIds;
|
private final EnumSet<GmsService> supportedServices;
|
||||||
|
|
||||||
public AbstractGmsServiceBroker(Integer supportedServiceId, Integer... supportedServiceIds) {
|
public AbstractGmsServiceBroker(EnumSet<GmsService> supportedServices) {
|
||||||
this(combine(supportedServiceId, supportedServiceIds));
|
this.supportedServices = supportedServices;
|
||||||
}
|
|
||||||
|
|
||||||
private static Set<Integer> combine(Integer i, Integer... is) {
|
|
||||||
Set<Integer> integers = new HashSet<Integer>(Arrays.asList(is));
|
|
||||||
integers.add(i);
|
|
||||||
return integers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractGmsServiceBroker(Set<Integer> supportedServiceIds) {
|
|
||||||
this.supportedServiceIds = supportedServiceIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -62,21 +48,21 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
Bundle extras = params == null ? new Bundle() : params;
|
Bundle extras = params == null ? new Bundle() : params;
|
||||||
extras.putString("auth_package", authPackage);
|
extras.putString("auth_package", authPackage);
|
||||||
callGetService(Services.PLUS.SERVICE_ID, callback, versionCode, packageName, extras, accountName, scopes);
|
callGetService(GmsService.PLUS, callback, versionCode, packageName, extras, accountName, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getPanoramaService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getPanoramaService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.PANORAMA.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.PANORAMA, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getAppDataSearchService(IGmsCallbacks callback, int versionCode, String packageName)
|
public void getAppDataSearchService(IGmsCallbacks callback, int versionCode, String packageName)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
callGetService(Services.INDEX.SERVICE_ID, callback, versionCode, packageName);
|
callGetService(GmsService.INDEX, callback, versionCode, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -89,28 +75,28 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
@Override
|
@Override
|
||||||
public void getPeopleService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getPeopleService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.PEOPLE.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.PEOPLE, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getReportingService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getReportingService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.LOCATION_REPORTING.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.LOCATION_REPORTING, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getLocationService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getLocationService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.LOCATION.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.LOCATION, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getGoogleLocationManagerService(IGmsCallbacks callback, int versionCode,
|
public void getGoogleLocationManagerService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName, Bundle params) throws RemoteException {
|
String packageName, Bundle params) throws RemoteException {
|
||||||
callGetService(Services.LOCATION_MANAGER.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.LOCATION_MANAGER, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -123,70 +109,70 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
extras.putString("com.google.android.gms.games.key.gamePackageName", gamePackageName);
|
extras.putString("com.google.android.gms.games.key.gamePackageName", gamePackageName);
|
||||||
extras.putString("com.google.android.gms.games.key.desiredLocale", desiredLocale);
|
extras.putString("com.google.android.gms.games.key.desiredLocale", desiredLocale);
|
||||||
//extras.putParcelable("com.google.android.gms.games.key.popupWindowToken", popupWindowToken);
|
//extras.putParcelable("com.google.android.gms.games.key.popupWindowToken", popupWindowToken);
|
||||||
callGetService(Services.GAMES.SERVICE_ID, callback, versionCode, packageName, extras, accountName, scopes);
|
callGetService(GmsService.GAMES, callback, versionCode, packageName, extras, accountName, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getAppStateService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getAppStateService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
String accountName, String[] scopes) throws RemoteException {
|
String accountName, String[] scopes) throws RemoteException {
|
||||||
callGetService(Services.APPSTATE.SERVICE_ID, callback, versionCode, packageName, null, accountName, scopes);
|
callGetService(GmsService.APPSTATE, callback, versionCode, packageName, null, accountName, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getPlayLogService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getPlayLogService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.PLAY_LOG.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.PLAY_LOG, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getAdMobService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getAdMobService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.ADREQUEST.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.ADREQUEST, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getDroidGuardService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getDroidGuardService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.DROIDGUARD.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.DROIDGUARD, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getLockboxService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getLockboxService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.LOCKBOX.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.LOCKBOX, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getCastMirroringService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getCastMirroringService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.CAST_MIRRORING.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.CAST_MIRRORING, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getNetworkQualityService(IGmsCallbacks callback, int versionCode,
|
public void getNetworkQualityService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName, Bundle params) throws RemoteException {
|
String packageName, Bundle params) throws RemoteException {
|
||||||
callGetService(Services.NETWORK_QUALITY.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.NETWORK_QUALITY, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getGoogleIdentityService(IGmsCallbacks callback, int versionCode,
|
public void getGoogleIdentityService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName, Bundle params) throws RemoteException {
|
String packageName, Bundle params) throws RemoteException {
|
||||||
callGetService(Services.ACCOUNT.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.ACCOUNT, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getGoogleFeedbackService(IGmsCallbacks callback, int versionCode,
|
public void getGoogleFeedbackService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName, Bundle params) throws RemoteException {
|
String packageName, Bundle params) throws RemoteException {
|
||||||
callGetService(Services.FEEDBACK.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.FEEDBACK, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
|
@ -200,55 +186,55 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
@Override
|
@Override
|
||||||
public void getDriveService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getDriveService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
String[] scopes, String accountName, Bundle params) throws RemoteException {
|
String[] scopes, String accountName, Bundle params) throws RemoteException {
|
||||||
callGetService(Services.DRIVE.SERVICE_ID, callback, versionCode, packageName, params, accountName, scopes);
|
callGetService(GmsService.DRIVE, callback, versionCode, packageName, params, accountName, scopes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getLightweightAppDataSearchService(IGmsCallbacks callback, int versionCode,
|
public void getLightweightAppDataSearchService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName) throws RemoteException {
|
String packageName) throws RemoteException {
|
||||||
callGetService(Services.LIGHTWEIGHT_INDEX.SERVICE_ID, callback, versionCode, packageName);
|
callGetService(GmsService.LIGHTWEIGHT_INDEX, callback, versionCode, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getSearchAdministrationService(IGmsCallbacks callback, int versionCode,
|
public void getSearchAdministrationService(IGmsCallbacks callback, int versionCode,
|
||||||
String packageName) throws RemoteException {
|
String packageName) throws RemoteException {
|
||||||
callGetService(Services.SEARCH_ADMINISTRATION.SERVICE_ID, callback, versionCode, packageName);
|
callGetService(GmsService.SEARCH_ADMINISTRATION, callback, versionCode, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getAutoBackupService(IGmsCallbacks callback, int versionCode, String packageName,
|
public void getAutoBackupService(IGmsCallbacks callback, int versionCode, String packageName,
|
||||||
Bundle params) throws RemoteException {
|
Bundle params) throws RemoteException {
|
||||||
callGetService(Services.PHOTO_AUTO_BACKUP.SERVICE_ID, callback, versionCode, packageName, params);
|
callGetService(GmsService.PHOTO_AUTO_BACKUP, callback, versionCode, packageName, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getAddressService(IGmsCallbacks callback, int versionCode, String packageName)
|
public void getAddressService(IGmsCallbacks callback, int versionCode, String packageName)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
callGetService(Services.ADDRESS.SERVICE_ID, callback, versionCode, packageName);
|
callGetService(GmsService.ADDRESS, callback, versionCode, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public void getWalletServiceWithPackageName(IGmsCallbacks callback, int versionCode, String packageName) throws RemoteException {
|
public void getWalletServiceWithPackageName(IGmsCallbacks callback, int versionCode, String packageName) throws RemoteException {
|
||||||
callGetService(Services.WALLET.SERVICE_ID, callback, versionCode, packageName);
|
callGetService(GmsService.WALLET, callback, versionCode, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callGetService(int serviceId, IGmsCallbacks callback, int gmsVersion,
|
private void callGetService(GmsService service, IGmsCallbacks callback, int gmsVersion,
|
||||||
String packageName) throws RemoteException {
|
String packageName) throws RemoteException {
|
||||||
callGetService(serviceId, callback, gmsVersion, packageName, null);
|
callGetService(service, callback, gmsVersion, packageName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callGetService(int serviceId, IGmsCallbacks callback, int gmsVersion,
|
private void callGetService(GmsService service, IGmsCallbacks callback, int gmsVersion,
|
||||||
String packageName, Bundle extras) throws RemoteException {
|
String packageName, Bundle extras) throws RemoteException {
|
||||||
callGetService(serviceId, callback, gmsVersion, packageName, extras, null, null);
|
callGetService(service, callback, gmsVersion, packageName, extras, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callGetService(int serviceId, IGmsCallbacks callback, int gmsVersion, String packageName, Bundle extras, String accountName, String[] scopes) throws RemoteException {
|
private void callGetService(GmsService service, IGmsCallbacks callback, int gmsVersion, String packageName, Bundle extras, String accountName, String[] scopes) throws RemoteException {
|
||||||
GetServiceRequest request = new GetServiceRequest(serviceId);
|
GetServiceRequest request = new GetServiceRequest(service.SERVICE_ID);
|
||||||
request.gmsVersion = gmsVersion;
|
request.gmsVersion = gmsVersion;
|
||||||
request.packageName = packageName;
|
request.packageName = packageName;
|
||||||
request.extras = extras;
|
request.extras = extras;
|
||||||
|
@ -267,15 +253,16 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getService(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void getService(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
||||||
if (supportedServiceIds.contains(request.serviceId) || supportedServiceIds.contains(ID_ACCEPT_ALL)) {
|
GmsService gmsService = GmsService.byServiceId(request.serviceId);
|
||||||
handleServiceRequest(callback, request);
|
if ((supportedServices.contains(gmsService)) || supportedServices.contains(GmsService.ANY)) {
|
||||||
|
handleServiceRequest(callback, request, gmsService);
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Service not supported: " + request);
|
Log.d(TAG, "Service not supported: " + request);
|
||||||
throw new IllegalArgumentException("Service not supported: " + request.serviceId);
|
throw new IllegalArgumentException("Service not supported: " + request.serviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException;
|
public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateAccount(IGmsCallbacks callback, ValidateAccountRequest request) throws RemoteException {
|
public void validateAccount(IGmsCallbacks callback, ValidateAccountRequest request) throws RemoteException {
|
||||||
|
|
|
@ -26,22 +26,29 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
import com.google.android.gms.common.internal.IGmsServiceBroker;
|
||||||
|
|
||||||
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
public abstract class BaseService extends Service {
|
public abstract class BaseService extends Service {
|
||||||
private final IGmsServiceBroker broker;
|
private final IGmsServiceBroker broker;
|
||||||
protected final String TAG;
|
protected final String TAG;
|
||||||
|
|
||||||
public BaseService(String tag, Integer supportedServiceId, Integer... supportedServiceIds) {
|
public BaseService(String tag, GmsService supportedService, GmsService... supportedServices) {
|
||||||
this.TAG = tag;
|
this.TAG = tag;
|
||||||
broker = new AbstractGmsServiceBroker(supportedServiceId, supportedServiceIds) {
|
EnumSet<GmsService> services = EnumSet.of(supportedService);
|
||||||
|
services.addAll(Arrays.asList(supportedServices));
|
||||||
|
broker = new AbstractGmsServiceBroker(services) {
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
try {
|
try {
|
||||||
request.extras.keySet(); // call to unparcel()
|
request.extras.keySet(); // call to unparcel()
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Sometimes we need to define the correct ClassLoader before unparcel(). Ignore those.
|
// Sometimes we need to define the correct ClassLoader before unparcel(). Ignore those.
|
||||||
}
|
}
|
||||||
Log.d(TAG, "bound by: " + request);
|
Log.d(TAG, "bound by: " + request);
|
||||||
BaseService.this.handleServiceRequest(callback, request);
|
BaseService.this.handleServiceRequest(callback, request, service);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -52,5 +59,5 @@ public abstract class BaseService extends Service {
|
||||||
return broker.asBinder();
|
return broker.asBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException;
|
public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,13 +22,15 @@ import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class DummyService extends BaseService {
|
public class DummyService extends BaseService {
|
||||||
public DummyService() {
|
public DummyService() {
|
||||||
super("GmsDummySvc", AbstractGmsServiceBroker.ID_ACCEPT_ALL);
|
super("GmsDummySvc", GmsService.ANY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,16 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class GService extends BaseService {
|
public class GService extends BaseService {
|
||||||
|
|
||||||
public GService() {
|
public GService() {
|
||||||
super("GmsAdsGSvc", Services.GSERVICES.SERVICE_ID, Services.ADREQUEST.SERVICE_ID);
|
super("GmsAdsGSvc", GmsService.GSERVICES, GmsService.ADREQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class AccountContentProvider extends ContentProvider {
|
||||||
if (!PackageUtils.callerHasExtendedAccess(getContext())) {
|
if (!PackageUtils.callerHasExtendedAccess(getContext())) {
|
||||||
String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
||||||
if (packagesForUid != null && packagesForUid.length != 0)
|
if (packagesForUid != null && packagesForUid.length != 0)
|
||||||
Log.w(TAG, "Not granting access to " + Arrays.toString(packagesForUid)
|
Log.w(TAG, "Not granting extended access to " + Arrays.toString(packagesForUid)
|
||||||
+ ", signature: " + PackageUtils.firstSignatureDigest(getContext(), packagesForUid[0]));
|
+ ", signature: " + PackageUtils.firstSignatureDigest(getContext(), packagesForUid[0]));
|
||||||
if (getContext().checkCallingPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED)
|
if (getContext().checkCallingPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED)
|
||||||
throw new SecurityException("Access denied, missing GET_ACCOUNTS or EXTENDED_ACCESS permission");
|
throw new SecurityException("Access denied, missing GET_ACCOUNTS or EXTENDED_ACCESS permission");
|
||||||
|
|
|
@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class SignInService extends BaseService {
|
public class SignInService extends BaseService {
|
||||||
public SignInService() {
|
public SignInService() {
|
||||||
super("GmsSignInSvc", Services.SIGN_IN.SERVICE_ID);
|
super("GmsSignInSvc", GmsService.SIGN_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class CarService extends BaseService {
|
public class CarService extends BaseService {
|
||||||
public CarService() {
|
public CarService() {
|
||||||
super("GmsCarSvc", Services.CAR.SERVICE_ID);
|
super("GmsCarSvc", GmsService.CAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class ClearcutLoggerService extends BaseService {
|
public class ClearcutLoggerService extends BaseService {
|
||||||
private ClearcutLoggerServiceImpl clearcutService = new ClearcutLoggerServiceImpl();
|
private ClearcutLoggerServiceImpl clearcutService = new ClearcutLoggerServiceImpl();
|
||||||
|
|
||||||
public ClearcutLoggerService() {
|
public ClearcutLoggerService() {
|
||||||
super("GmsClearcutSvc", Services.CLEARCUT_LOGGER.SERVICE_ID);
|
super("GmsClearcutSvc", GmsService.CLEARCUT_LOGGER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, clearcutService.asBinder(), null);
|
callback.onPostInitComplete(0, clearcutService.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013-2016 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.common;
|
||||||
|
|
||||||
|
import android.os.IInterface;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
public class MultiListenerProxy<T extends IInterface> implements InvocationHandler {
|
||||||
|
private static final String TAG = "GmsMultiListener";
|
||||||
|
|
||||||
|
public static <T extends IInterface> T get(Class<T> tClass, final Collection<T> listeners) {
|
||||||
|
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new MultiListenerProxy<T>(listeners));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Collection<T> listeners;
|
||||||
|
|
||||||
|
private MultiListenerProxy(Collection<T> listeners) {
|
||||||
|
this.listeners = listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||||
|
for (T listener : new HashSet<T>(listeners)) {
|
||||||
|
try {
|
||||||
|
method.invoke(listener, args);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
listeners.remove(listener);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
Log.w(TAG, e.getTargetException());
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1;
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_SIGNATURE_SHA1;
|
||||||
|
|
||||||
public class PackageUtils {
|
public class PackageUtils {
|
||||||
|
@ -37,7 +38,8 @@ public class PackageUtils {
|
||||||
"58e1c4133f7441ec3d2c270270a14802da47ba0e" /* Android Wear */,
|
"58e1c4133f7441ec3d2c270270a14802da47ba0e" /* Android Wear */,
|
||||||
"46f6c8987311e131f4f558d8e0ae145bebab6da3" /* Google Classroom */,
|
"46f6c8987311e131f4f558d8e0ae145bebab6da3" /* Google Classroom */,
|
||||||
"24bb24c05e47e0aefa68a58a766179d9b613a600" /* Google Fit/Glass */,
|
"24bb24c05e47e0aefa68a58a766179d9b613a600" /* Google Fit/Glass */,
|
||||||
"aa87ce1260c008d801197bb4ecea4ab8929da246" /* Google Inbox */};
|
"aa87ce1260c008d801197bb4ecea4ab8929da246" /* Google Inbox */,
|
||||||
|
"35b438fe1bc69d975dc8702dc16ab69ebf65f26f" /* Waze */};
|
||||||
|
|
||||||
public static boolean isGoogleSignedPackages(Context context, String packageName) {
|
public static boolean isGoogleSignedPackages(Context context, String packageName) {
|
||||||
return Arrays.asList(KNOWN_GOOGLE_SIGNATURES).contains(firstSignatureDigest(context, packageName));
|
return Arrays.asList(KNOWN_GOOGLE_SIGNATURES).contains(firstSignatureDigest(context, packageName));
|
||||||
|
@ -50,8 +52,13 @@ public class PackageUtils {
|
||||||
|
|
||||||
public static boolean callerHasExtendedAccess(Context context) {
|
public static boolean callerHasExtendedAccess(Context context) {
|
||||||
String[] packagesForUid = context.getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
String[] packagesForUid = context.getPackageManager().getPackagesForUid(Binder.getCallingUid());
|
||||||
return (packagesForUid != null && packagesForUid.length != 0 && isGoogleSignedPackages(context, packagesForUid[0])) ||
|
if (packagesForUid != null && packagesForUid.length != 0) {
|
||||||
context.checkCallingPermission(Manifest.permission.EXTENDED_ACCESS) == PackageManager.PERMISSION_GRANTED;
|
for (String packageName : packagesForUid) {
|
||||||
|
if (isGoogleSignedPackages(context, packageName) || GMS_PACKAGE_NAME.equals(packageName))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context.checkCallingPermission(Manifest.permission.EXTENDED_ACCESS) == PackageManager.PERMISSION_GRANTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkPackageUid(Context context, String packageName, int callingUid) {
|
public static void checkPackageUid(Context context, String packageName, int callingUid) {
|
||||||
|
|
|
@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class DriveApiService extends BaseService {
|
public class DriveApiService extends BaseService {
|
||||||
private DriveServiceImpl impl = new DriveServiceImpl();
|
private DriveServiceImpl impl = new DriveServiceImpl();
|
||||||
|
|
||||||
public DriveApiService() {
|
public DriveApiService() {
|
||||||
super("GmsDriveApiSvc", Services.DRIVE.SERVICE_ID);
|
super("GmsDriveApiSvc", GmsService.DRIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, impl.asBinder(), null);
|
callback.onPostInitComplete(0, impl.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,16 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class DroidGuardService extends BaseService {
|
public class DroidGuardService extends BaseService {
|
||||||
|
|
||||||
public DroidGuardService() {
|
public DroidGuardService() {
|
||||||
super("GmsDroidGuardSvc", Services.DROIDGUARD.SERVICE_ID);
|
super("GmsDroidGuardSvc", GmsService.DROIDGUARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class FeedbackService extends BaseService {
|
public class FeedbackService extends BaseService {
|
||||||
public FeedbackService() {
|
public FeedbackService() {
|
||||||
super("GmsFeedbackSvc", Services.FEEDBACK.SERVICE_ID);
|
super("GmsFeedbackSvc", GmsService.FEEDBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services.GAMES;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
import static org.microg.gms.common.Constants.GMS_PACKAGE_NAME;
|
||||||
|
@ -38,11 +38,11 @@ public class GamesStubService extends BaseService {
|
||||||
public static final String PARAM_GAME_PACKAGE_NAME = "com.google.android.gms.games.key.gamePackageName";
|
public static final String PARAM_GAME_PACKAGE_NAME = "com.google.android.gms.games.key.gamePackageName";
|
||||||
|
|
||||||
public GamesStubService() {
|
public GamesStubService() {
|
||||||
super("GmsGamesSvc", GAMES.SERVICE_ID);
|
super("GmsGamesSvc", GmsService.GAMES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
String packageName = null;
|
String packageName = null;
|
||||||
if (request.extras != null) {
|
if (request.extras != null) {
|
||||||
packageName = request.extras.getString(PARAM_GAME_PACKAGE_NAME);
|
packageName = request.extras.getString(PARAM_GAME_PACKAGE_NAME);
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class McsService extends Service implements Handler.Callback {
|
||||||
private static final String PREF_GCM_HEARTBEAT = "gcm_heartbeat_interval";
|
private static final String PREF_GCM_HEARTBEAT = "gcm_heartbeat_interval";
|
||||||
private static final int WAKELOCK_TIMEOUT = 5000;
|
private static final int WAKELOCK_TIMEOUT = 5000;
|
||||||
|
|
||||||
public static int heartbeatMs = 60000;
|
public static int heartbeatMs = 120000;
|
||||||
private static long lastHeartbeatAckElapsedRealtime = -1;
|
private static long lastHeartbeatAckElapsedRealtime = -1;
|
||||||
|
|
||||||
private static Socket sslSocket;
|
private static Socket sslSocket;
|
||||||
|
|
|
@ -17,13 +17,14 @@
|
||||||
package org.microg.gms.icing;
|
package org.microg.gms.icing;
|
||||||
|
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
import com.google.android.gms.common.internal.GetServiceRequest;
|
import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class IndexService extends BaseService {
|
public class IndexService extends BaseService {
|
||||||
private AppDataSearchImpl appDataSearch = new AppDataSearchImpl();
|
private AppDataSearchImpl appDataSearch = new AppDataSearchImpl();
|
||||||
|
@ -33,30 +34,31 @@ public class IndexService extends BaseService {
|
||||||
|
|
||||||
public IndexService() {
|
public IndexService() {
|
||||||
super("GmsIcingIndexSvc",
|
super("GmsIcingIndexSvc",
|
||||||
Services.INDEX.SERVICE_ID, Services.SEARCH_ADMINISTRATION.SERVICE_ID,
|
GmsService.INDEX, GmsService.SEARCH_ADMINISTRATION, GmsService.SEARCH_CORPORA,
|
||||||
Services.SEARCH_CORPORA.SERVICE_ID, Services.SEARCH_GLOBAL.SERVICE_ID,
|
GmsService.SEARCH_GLOBAL, GmsService.SEARCH_IME, GmsService.SEARCH_QUERIES);
|
||||||
Services.SEARCH_IME.SERVICE_ID, Services.SEARCH_QUERIES.SERVICE_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
switch (request.serviceId) {
|
switch (service) {
|
||||||
case Services.INDEX.SERVICE_ID:
|
case INDEX:
|
||||||
callback.onPostInitComplete(0, appDataSearch.asBinder(), null);
|
callback.onPostInitComplete(0, appDataSearch.asBinder(), null);
|
||||||
break;
|
break;
|
||||||
case Services.SEARCH_ADMINISTRATION.SERVICE_ID:
|
case SEARCH_ADMINISTRATION:
|
||||||
|
Log.w(TAG, "Service not yet implemented: " + service);
|
||||||
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
||||||
break;
|
break;
|
||||||
case Services.SEARCH_QUERIES.SERVICE_ID:
|
case SEARCH_QUERIES:
|
||||||
callback.onPostInitComplete(0, searchQueries.asBinder(), null);
|
callback.onPostInitComplete(0, searchQueries.asBinder(), null);
|
||||||
break;
|
break;
|
||||||
case Services.SEARCH_GLOBAL.SERVICE_ID:
|
case SEARCH_GLOBAL:
|
||||||
callback.onPostInitComplete(0, globalSearchAdmin.asBinder(), null);
|
callback.onPostInitComplete(0, globalSearchAdmin.asBinder(), null);
|
||||||
break;
|
break;
|
||||||
case Services.SEARCH_CORPORA.SERVICE_ID:
|
case SEARCH_CORPORA:
|
||||||
callback.onPostInitComplete(0, searchCorpora.asBinder(), null);
|
callback.onPostInitComplete(0, searchCorpora.asBinder(), null);
|
||||||
break;
|
break;
|
||||||
case Services.SEARCH_IME.SERVICE_ID:
|
case SEARCH_IME:
|
||||||
|
Log.w(TAG, "Service not yet implemented: " + service);
|
||||||
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class LightweightIndexService extends BaseService {
|
public class LightweightIndexService extends BaseService {
|
||||||
private LightweightAppDataSearchImpl appDataSearch = new LightweightAppDataSearchImpl();
|
private LightweightAppDataSearchImpl appDataSearch = new LightweightAppDataSearchImpl();
|
||||||
|
|
||||||
public LightweightIndexService() {
|
public LightweightIndexService() {
|
||||||
super("GmsIcingLightIndexSvc", Services.LIGHTWEIGHT_INDEX.SERVICE_ID);
|
super("GmsIcingLightIndexSvc", GmsService.LIGHTWEIGHT_INDEX);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, appDataSearch.asBinder(), null);
|
callback.onPostInitComplete(0, appDataSearch.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,18 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class GoogleLocationManagerService extends BaseService {
|
public class GoogleLocationManagerService extends BaseService {
|
||||||
private GoogleLocationManagerServiceImpl impl = new GoogleLocationManagerServiceImpl(this);
|
private GoogleLocationManagerServiceImpl impl = new GoogleLocationManagerServiceImpl(this);
|
||||||
|
|
||||||
public GoogleLocationManagerService() {
|
public GoogleLocationManagerService() {
|
||||||
super("GmsLocManagerSvc", Services.LOCATION_MANAGER.SERVICE_ID,
|
super("GmsLocManagerSvc", GmsService.LOCATION_MANAGER, GmsService.GEODATA, GmsService.PLACE_DETECTION);
|
||||||
Services.GEODATA.SERVICE_ID, Services.PLACE_DETECTION.SERVICE_ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, impl.asBinder(), null);
|
callback.onPostInitComplete(0, impl.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class ReportingAndroidService extends BaseService {
|
public class ReportingAndroidService extends BaseService {
|
||||||
private ReportingServiceImpl reportingService = new ReportingServiceImpl();
|
private ReportingServiceImpl reportingService = new ReportingServiceImpl();
|
||||||
|
|
||||||
public ReportingAndroidService() {
|
public ReportingAndroidService() {
|
||||||
super("GmsLocReportingSvc", Services.LOCATION_REPORTING.SERVICE_ID);
|
super("GmsLocReportingSvc", GmsService.LOCATION_REPORTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, reportingService.asBinder(), null);
|
callback.onPostInitComplete(0, reportingService.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class PeopleService extends BaseService {
|
public class PeopleService extends BaseService {
|
||||||
private PeopleServiceImpl impl = new PeopleServiceImpl(this);
|
private PeopleServiceImpl impl = new PeopleServiceImpl(this);
|
||||||
|
|
||||||
public PeopleService() {
|
public PeopleService() {
|
||||||
super("GmsPeopleSvc", Services.PEOPLE.SERVICE_ID);
|
super("GmsPeopleSvc", GmsService.PEOPLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, impl.asBinder(), null);
|
callback.onPostInitComplete(0, impl.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class GeoDataService extends BaseService {
|
public class GeoDataService extends BaseService {
|
||||||
public GeoDataService() {
|
public GeoDataService() {
|
||||||
super("GmsPlcGeoSvc", Services.GEODATA.SERVICE_ID);
|
super("GmsPlcGeoSvc", GmsService.GEODATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class PlaceDetectionService extends BaseService {
|
public class PlaceDetectionService extends BaseService {
|
||||||
public PlaceDetectionService() {
|
public PlaceDetectionService() {
|
||||||
super("GmsPlcDtctSvc", Services.PLACE_DETECTION.SERVICE_ID);
|
super("GmsPlcDtctSvc", GmsService.PLACE_DETECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
Log.d(TAG, "unimplemented Method: handleServiceRequest");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,18 +22,18 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
import org.microg.gms.common.Services;
|
import org.microg.gms.common.GmsService;
|
||||||
|
|
||||||
public class PlayLogService extends BaseService {
|
public class PlayLogService extends BaseService {
|
||||||
|
|
||||||
private PlayLogServiceImpl playLogService = new PlayLogServiceImpl();
|
private PlayLogServiceImpl playLogService = new PlayLogServiceImpl();
|
||||||
|
|
||||||
public PlayLogService() {
|
public PlayLogService() {
|
||||||
super("GmsPlayLogSvc", Services.PLAY_LOG.SERVICE_ID);
|
super("GmsPlayLogSvc", GmsService.PLAY_LOG);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
callback.onPostInitComplete(0, playLogService.asBinder(), null);
|
callback.onPostInitComplete(0, playLogService.asBinder(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,14 +113,14 @@ public class SettingsActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void collectLibraries(List<Library> libraries) {
|
protected void collectLibraries(List<Library> libraries) {
|
||||||
libraries.add(new Library("org.microg.gms.api", "microG GmsApi", "Apache License 2.0, Copyright © microG Team"));
|
libraries.add(new Library("org.microg.gms.api", "microG GmsApi", "Apache License 2.0 by microG Team"));
|
||||||
libraries.add(new Library("org.microg.safeparcel", "microG SafeParcel", "Apache License 2.0, Copyright © microG Team"));
|
libraries.add(new Library("org.microg.safeparcel", "microG SafeParcel", "Apache License 2.0 by microG Team"));
|
||||||
libraries.add(new Library("org.microg.nlp", "microG UnifiedNlp", "Apache License 2.0, Copyright © microG Team"));
|
libraries.add(new Library("org.microg.nlp", "microG UnifiedNlp", "Apache License 2.0 by microG Team"));
|
||||||
libraries.add(new Library("org.microg.nlp.api", "microG UnifiedNlp Api", "Apache License 2.0, Copyright © microG Team"));
|
libraries.add(new Library("org.microg.nlp.api", "microG UnifiedNlp Api", "Apache License 2.0, by microG Team"));
|
||||||
libraries.add(new Library("org.microg.wearable", "microG Wearable", "Apache License 2.0, Copyright © microG Team"));
|
libraries.add(new Library("org.microg.wearable", "microG Wearable", "Apache License 2.0 by microG Team"));
|
||||||
libraries.add(new Library("de.hdodenhof.circleimageview", "CircleImageView", "Apache License 2.0, Copyright © Henning Dodenhof"));
|
libraries.add(new Library("de.hdodenhof.circleimageview", "CircleImageView", "Apache License 2.0 by Henning Dodenhof"));
|
||||||
libraries.add(new Library("org.oscim.android", "<vector<tile>>map", "GNU LGPLv3, Copyright © Hannes Janetzek"));
|
libraries.add(new Library("org.oscim.android", "<vector<tile>>map", "GNU LGPLv3 by Hannes Janetzek"));
|
||||||
libraries.add(new Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0, Copyright © Square Inc."));
|
libraries.add(new Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0 by Square Inc."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,4 +136,20 @@ public class DataItemRecord {
|
||||||
record.signatureDigest = setDataItem.signatureDigest;
|
record.signatureDigest = setDataItem.signatureDigest;
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("DataItemRecord{");
|
||||||
|
sb.append("dataItem=").append(dataItem);
|
||||||
|
sb.append(", source='").append(source).append('\'');
|
||||||
|
sb.append(", seqId=").append(seqId);
|
||||||
|
sb.append(", v1SeqId=").append(v1SeqId);
|
||||||
|
sb.append(", lastModified=").append(lastModified);
|
||||||
|
sb.append(", deleted=").append(deleted);
|
||||||
|
sb.append(", assetsAreReady=").append(assetsAreReady);
|
||||||
|
sb.append(", packageName='").append(packageName).append('\'');
|
||||||
|
sb.append(", signatureDigest='").append(signatureDigest).append('\'');
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import android.util.Log;
|
||||||
import com.google.android.gms.wearable.Asset;
|
import com.google.android.gms.wearable.Asset;
|
||||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
|
||||||
|
|
||||||
import org.microg.gms.checkin.LastCheckinInfo;
|
import org.microg.gms.checkin.LastCheckinInfo;
|
||||||
import org.microg.gms.common.Build;
|
import org.microg.gms.common.Build;
|
||||||
|
@ -44,14 +43,15 @@ import java.util.Arrays;
|
||||||
|
|
||||||
public class MessageHandler extends ServerMessageListener {
|
public class MessageHandler extends ServerMessageListener {
|
||||||
private static final String TAG = "GmsWearMsgHandler";
|
private static final String TAG = "GmsWearMsgHandler";
|
||||||
private final WearableServiceImpl service;
|
private final WearableImpl wearable;
|
||||||
private final ConnectionConfiguration config;
|
private final String thisNodeId;
|
||||||
|
private String peerNodeId;
|
||||||
|
|
||||||
public MessageHandler(WearableServiceImpl service, ConnectionConfiguration config) {
|
public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) {
|
||||||
this(service, config, new Build().model, config.nodeId, LastCheckinInfo.read(service.getContext()).androidId);
|
this(wearable, config, new Build().model, config.nodeId, LastCheckinInfo.read(wearable.getContext()).androidId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MessageHandler(WearableServiceImpl service, ConnectionConfiguration config, String name, String networkId, long androidId) {
|
private MessageHandler(WearableImpl wearable, ConnectionConfiguration config, String name, String networkId, long androidId) {
|
||||||
super(new Connect.Builder()
|
super(new Connect.Builder()
|
||||||
.name(name)
|
.name(name)
|
||||||
.id(config.nodeId)
|
.id(config.nodeId)
|
||||||
|
@ -60,24 +60,23 @@ public class MessageHandler extends ServerMessageListener {
|
||||||
.unknown4(3)
|
.unknown4(3)
|
||||||
.unknown5(1)
|
.unknown5(1)
|
||||||
.build());
|
.build());
|
||||||
this.service = service;
|
this.wearable = wearable;
|
||||||
this.config = config;
|
this.thisNodeId = config.nodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConnect(Connect connect) {
|
public void onConnect(Connect connect) {
|
||||||
super.onConnect(connect);
|
super.onConnect(connect);
|
||||||
config.peerNodeId = connect.id;
|
peerNodeId = connect.id;
|
||||||
config.connected = true;
|
wearable.onConnectReceived(getConnection(), thisNodeId, connect);
|
||||||
service.onPeerConnected(new NodeParcelable(connect.id, connect.name));
|
|
||||||
try {
|
try {
|
||||||
getConnection().writeMessage(new RootMessage.Builder().syncStart(new SyncStart.Builder()
|
getConnection().writeMessage(new RootMessage.Builder().syncStart(new SyncStart.Builder()
|
||||||
.receivedSeqId(-1L)
|
.receivedSeqId(-1L)
|
||||||
.version(2)
|
.version(2)
|
||||||
.syncTable(Arrays.asList(
|
.syncTable(Arrays.asList(
|
||||||
new SyncTableEntry.Builder().key("cloud").value(1L).build(),
|
new SyncTableEntry.Builder().key("cloud").value(1L).build(),
|
||||||
new SyncTableEntry.Builder().key(config.nodeId).value(service.getCurrentSeqId(config.nodeId)).build(), // TODO
|
new SyncTableEntry.Builder().key(thisNodeId).value(wearable.getCurrentSeqId(thisNodeId)).build(), // TODO
|
||||||
new SyncTableEntry.Builder().key(config.peerNodeId).value(service.getCurrentSeqId(config.peerNodeId)).build() // TODO
|
new SyncTableEntry.Builder().key(peerNodeId).value(wearable.getCurrentSeqId(peerNodeId)).build() // TODO
|
||||||
)).build()).build());
|
)).build()).build());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
|
@ -93,7 +92,7 @@ public class MessageHandler extends ServerMessageListener {
|
||||||
} else {
|
} else {
|
||||||
asset = Asset.createFromRef(setAsset.digest);
|
asset = Asset.createFromRef(setAsset.digest);
|
||||||
}
|
}
|
||||||
service.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
|
wearable.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -115,34 +114,34 @@ public class MessageHandler extends ServerMessageListener {
|
||||||
boolean hasLocalNode = false;
|
boolean hasLocalNode = false;
|
||||||
if (syncStart.syncTable != null) {
|
if (syncStart.syncTable != null) {
|
||||||
for (SyncTableEntry entry : syncStart.syncTable) {
|
for (SyncTableEntry entry : syncStart.syncTable) {
|
||||||
service.syncToPeer(getConnection(), entry.key, entry.value);
|
wearable.syncToPeer(getConnection(), entry.key, entry.value);
|
||||||
if (service.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
|
if (wearable.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "No sync table given.");
|
Log.d(TAG, "No sync table given.");
|
||||||
}
|
}
|
||||||
if (!hasLocalNode) service.syncToPeer(getConnection(), service.getLocalNodeId(), 0);
|
if (!hasLocalNode) wearable.syncToPeer(getConnection(), wearable.getLocalNodeId(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetDataItem(SetDataItem setDataItem) {
|
public void onSetDataItem(SetDataItem setDataItem) {
|
||||||
Log.d(TAG, "onSetDataItem: " + setDataItem);
|
Log.d(TAG, "onSetDataItem: " + setDataItem);
|
||||||
service.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
|
wearable.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRcpRequest(Request rcpRequest) {
|
public void onRpcRequest(Request rpcRequest) {
|
||||||
Log.d(TAG, "onRcpRequest: " + rcpRequest);
|
Log.d(TAG, "onRpcRequest: " + rpcRequest);
|
||||||
if (TextUtils.isEmpty(rcpRequest.targetNodeId)) {
|
if (TextUtils.isEmpty(rpcRequest.targetNodeId)) {
|
||||||
// TODO: That's probably not how it should go!
|
// TODO: That's probably not how it should go!
|
||||||
MessageEventParcelable messageEvent = new MessageEventParcelable();
|
MessageEventParcelable messageEvent = new MessageEventParcelable();
|
||||||
messageEvent.data = rcpRequest.rawData != null ? rcpRequest.rawData.toByteArray() : null;
|
messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
|
||||||
messageEvent.path = rcpRequest.path;
|
messageEvent.path = rpcRequest.path;
|
||||||
messageEvent.requestId = rcpRequest.requestId + 31 * (rcpRequest.generation + 527);
|
messageEvent.requestId = rpcRequest.requestId + 31 * (rpcRequest.generation + 527);
|
||||||
messageEvent.sourceNodeId = TextUtils.isEmpty(rcpRequest.sourceNodeId) ? config.peerNodeId : rcpRequest.sourceNodeId;
|
messageEvent.sourceNodeId = TextUtils.isEmpty(rpcRequest.sourceNodeId) ? peerNodeId : rpcRequest.sourceNodeId;
|
||||||
|
|
||||||
service.onMessageReceived(messageEvent);
|
wearable.sendMessageReceived(messageEvent);
|
||||||
} else if (rcpRequest.targetNodeId.equals(config.peerNodeId)) {
|
} else if (rpcRequest.targetNodeId.equals(peerNodeId)) {
|
||||||
// Drop it, loop detection (yes we really need this in this protocol o.O)
|
// Drop it, loop detection (yes we really need this in this protocol o.O)
|
||||||
} else {
|
} else {
|
||||||
// TODO: find next hop (yes, wtf hops in a network usually consisting of two devices)
|
// TODO: find next hop (yes, wtf hops in a network usually consisting of two devices)
|
||||||
|
@ -157,7 +156,7 @@ public class MessageHandler extends ServerMessageListener {
|
||||||
@Override
|
@Override
|
||||||
public void onFilePiece(FilePiece filePiece) {
|
public void onFilePiece(FilePiece filePiece) {
|
||||||
Log.d(TAG, "onFilePiece: " + filePiece);
|
Log.d(TAG, "onFilePiece: " + filePiece);
|
||||||
service.handleFilePiece(getConnection(), filePiece.fileName, filePiece.piece.toByteArray(), filePiece.finalPiece ? filePiece.digest : null);
|
wearable.handleFilePiece(getConnection(), filePiece.fileName, filePiece.piece.toByteArray(), filePiece.finalPiece ? filePiece.digest : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.util.Log;
|
||||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||||
|
|
||||||
import org.microg.wearable.SocketWearableConnection;
|
import org.microg.wearable.SocketWearableConnection;
|
||||||
|
import org.microg.wearable.WearableConnection;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
@ -33,10 +34,11 @@ class NetworkConnectionThread extends Thread {
|
||||||
private ConnectionConfiguration config;
|
private ConnectionConfiguration config;
|
||||||
private ServerSocket socket;
|
private ServerSocket socket;
|
||||||
private MessageHandler handler;
|
private MessageHandler handler;
|
||||||
|
private WearableConnection wearableConnection;
|
||||||
|
|
||||||
public NetworkConnectionThread(WearableServiceImpl service, ConnectionConfiguration config) {
|
public NetworkConnectionThread(WearableImpl wearable, ConnectionConfiguration config) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.handler = new MessageHandler(service, config);
|
this.handler = new MessageHandler(wearable, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
|
@ -47,13 +49,17 @@ class NetworkConnectionThread extends Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WearableConnection getWearableConnection() {
|
||||||
|
return wearableConnection;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
socket = new ServerSocket(WEAR_TCP_PORT);
|
socket = new ServerSocket(WEAR_TCP_PORT);
|
||||||
Log.d(TAG, "Listening for connections on TCP :" + WEAR_TCP_PORT);
|
Log.d(TAG, "Listening for connections on TCP :" + WEAR_TCP_PORT);
|
||||||
Socket accepted = socket.accept();
|
Socket accepted = socket.accept();
|
||||||
new SocketWearableConnection(accepted, handler).run();
|
(wearableConnection = new SocketWearableConnection(accepted, handler)).run();
|
||||||
Log.d(TAG, "Connection terminated, me too");
|
Log.d(TAG, "Connection terminated, me too");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
|
|
|
@ -22,7 +22,6 @@ import android.database.Cursor;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.google.android.gms.wearable.Asset;
|
import com.google.android.gms.wearable.Asset;
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized long getAppKey(SQLiteDatabase db, String packageName, String signatureDigest) {
|
private static synchronized long getAppKey(SQLiteDatabase db, String packageName, String signatureDigest) {
|
||||||
Cursor cursor = db.rawQuery("SELECT _id FROM appkeys WHERE packageName=? AND signatureDigest=?", new String[]{packageName, signatureDigest});
|
Cursor cursor = db.rawQuery("SELECT _id FROM appkeys WHERE packageName=? AND signatureDigest=?", new String[]{packageName, signatureDigest});
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -143,22 +142,28 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||||
Log.d(TAG, "updateRecord no: " + record);
|
ContentValues cv = record.getContentValues();
|
||||||
|
db.update("dataitems", cv, "_id=?", new String[]{key});
|
||||||
|
finishRecord(db, key, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String insertRecord(SQLiteDatabase db, DataItemRecord record) {
|
private static String insertRecord(SQLiteDatabase db, DataItemRecord record) {
|
||||||
ContentValues contentValues = record.getContentValues();
|
ContentValues contentValues = record.getContentValues();
|
||||||
contentValues.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
|
contentValues.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
|
||||||
contentValues.put("host", record.dataItem.host);
|
contentValues.put("host", record.dataItem.host);
|
||||||
contentValues.put("path", record.dataItem.path);
|
contentValues.put("path", record.dataItem.path);
|
||||||
String key = Long.toString(db.insert("dataitems", "host", contentValues));
|
String key = Long.toString(db.insertWithOnConflict("dataitems", "host", contentValues, SQLiteDatabase.CONFLICT_REPLACE));
|
||||||
|
return finishRecord(db, key, record);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String finishRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||||
if (!record.deleted) {
|
if (!record.deleted) {
|
||||||
for (Map.Entry<String, Asset> asset : record.dataItem.getAssets().entrySet()) {
|
for (Map.Entry<String, Asset> asset : record.dataItem.getAssets().entrySet()) {
|
||||||
ContentValues assetValues = new ContentValues();
|
ContentValues assetValues = new ContentValues();
|
||||||
assetValues.put("assets_digest", asset.getValue().getDigest());
|
assetValues.put("assets_digest", asset.getValue().getDigest());
|
||||||
assetValues.put("dataitems_id", key);
|
assetValues.put("dataitems_id", key);
|
||||||
assetValues.put("assetname", asset.getKey());
|
assetValues.put("assetname", asset.getKey());
|
||||||
db.insert("assetrefs", "assetname", assetValues);
|
db.insertWithOnConflict("assetrefs", "assetname", assetValues, SQLiteDatabase.CONFLICT_IGNORE);
|
||||||
}
|
}
|
||||||
Cursor status = db.query("assetsReadyStatus", new String[]{"nowReady"}, "dataitems_id=?", new String[]{key}, null, null, null);
|
Cursor status = db.query("assetsReadyStatus", new String[]{"nowReady"}, "dataitems_id=?", new String[]{key}, null, null, null);
|
||||||
if (status.moveToNext()) {
|
if (status.moveToNext()) {
|
||||||
|
@ -230,14 +235,14 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||||
cv.put("digest", asset.getDigest());
|
cv.put("digest", asset.getDigest());
|
||||||
cv.put("dataPresent", dataPresent ? 1 : 0);
|
cv.put("dataPresent", dataPresent ? 1 : 0);
|
||||||
cv.put("timestampMs", System.currentTimeMillis());
|
cv.put("timestampMs", System.currentTimeMillis());
|
||||||
getWritableDatabase().insert("assets", null, cv);
|
getWritableDatabase().insertWithOnConflict("assets", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void allowAssetAccess(String digest, String packageName, String signatureDigest) {
|
public void allowAssetAccess(String digest, String packageName, String signatureDigest) {
|
||||||
SQLiteDatabase db = getWritableDatabase();
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
ContentValues cv = new ContentValues();
|
ContentValues cv = new ContentValues();
|
||||||
cv.put("assets_digest", digest);
|
cv.put("assets_digest", digest);
|
||||||
cv.put("appkeys_it", getAppKey(db, packageName, signatureDigest));
|
cv.put("appkeys_id", getAppKey(db, packageName, signatureDigest));
|
||||||
db.insert("assetsacls", null, cv);
|
db.insert("assetsacls", null, cv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,481 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2013-2016 microG Project Team
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.microg.gms.wearable;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.gms.common.data.DataHolder;
|
||||||
|
import com.google.android.gms.wearable.Asset;
|
||||||
|
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||||
|
import com.google.android.gms.wearable.Node;
|
||||||
|
import com.google.android.gms.wearable.internal.IWearableListener;
|
||||||
|
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
||||||
|
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||||
|
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||||
|
|
||||||
|
import org.microg.gms.common.MultiListenerProxy;
|
||||||
|
import org.microg.gms.common.PackageUtils;
|
||||||
|
import org.microg.gms.common.Utils;
|
||||||
|
import org.microg.wearable.WearableConnection;
|
||||||
|
import org.microg.wearable.proto.AckAsset;
|
||||||
|
import org.microg.wearable.proto.AppKey;
|
||||||
|
import org.microg.wearable.proto.AppKeys;
|
||||||
|
import org.microg.wearable.proto.Connect;
|
||||||
|
import org.microg.wearable.proto.FilePiece;
|
||||||
|
import org.microg.wearable.proto.RootMessage;
|
||||||
|
import org.microg.wearable.proto.SetAsset;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import okio.ByteString;
|
||||||
|
|
||||||
|
public class WearableImpl {
|
||||||
|
|
||||||
|
private static final String TAG = "GmsWear";
|
||||||
|
|
||||||
|
private static final String CLOCKWORK_NODE_PREFERENCES = "cw_node";
|
||||||
|
private static final String CLOCKWORK_NODE_PREFERENCE_NODE_ID = "node_id";
|
||||||
|
private static final String CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK = "nextSeqIdBlock";
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final NodeDatabaseHelper nodeDatabase;
|
||||||
|
private final ConfigurationDatabaseHelper configDatabase;
|
||||||
|
private final Set<IWearableListener> listeners = new HashSet<IWearableListener>();
|
||||||
|
private final Set<Node> connectedNodes = new HashSet<Node>();
|
||||||
|
private final Set<WearableConnection> activeConnections = new HashSet<WearableConnection>();
|
||||||
|
private NetworkConnectionThread nct;
|
||||||
|
private ConnectionConfiguration[] configurations;
|
||||||
|
private boolean configurationsUpdated = false;
|
||||||
|
|
||||||
|
private long seqIdBlock;
|
||||||
|
private long seqIdInBlock = -1;
|
||||||
|
|
||||||
|
public WearableImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase) {
|
||||||
|
this.context = context;
|
||||||
|
this.nodeDatabase = nodeDatabase;
|
||||||
|
this.configDatabase = configDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalNodeId() {
|
||||||
|
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
||||||
|
String nodeId = preferences.getString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, null);
|
||||||
|
if (nodeId == null) {
|
||||||
|
nodeId = UUID.randomUUID().toString();
|
||||||
|
preferences.edit().putString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, nodeId).apply();
|
||||||
|
}
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized long getNextSeqId() {
|
||||||
|
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
||||||
|
if (seqIdInBlock < 0) seqIdInBlock = 1000;
|
||||||
|
if (seqIdInBlock >= 1000) {
|
||||||
|
seqIdBlock = preferences.getLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, 100);
|
||||||
|
preferences.edit().putLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, seqIdBlock + seqIdInBlock).apply();
|
||||||
|
seqIdInBlock = 0;
|
||||||
|
}
|
||||||
|
return seqIdBlock + seqIdInBlock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
|
||||||
|
DataItemRecord record = new DataItemRecord();
|
||||||
|
record.packageName = packageName;
|
||||||
|
record.signatureDigest = signatureDigest;
|
||||||
|
record.deleted = false;
|
||||||
|
record.source = source;
|
||||||
|
record.dataItem = dataItem;
|
||||||
|
record.v1SeqId = getNextSeqId();
|
||||||
|
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
|
||||||
|
nodeDatabase.putRecord(record);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataItemRecord putDataItem(DataItemRecord record) {
|
||||||
|
nodeDatabase.putRecord(record);
|
||||||
|
try {
|
||||||
|
getAllListeners().onDataChanged(getDataItemsByUri(record.dataItem.uri, record.packageName));
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Asset prepareAsset(String packageName, Asset asset) {
|
||||||
|
if (asset.getFd() != null && asset.data == null) {
|
||||||
|
try {
|
||||||
|
asset.data = Utils.readStreamToEnd(new FileInputStream(asset.getFd().getFileDescriptor()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (asset.data != null) {
|
||||||
|
String digest = calculateDigest(asset.data);
|
||||||
|
File assetFile = createAssetFile(digest);
|
||||||
|
boolean success = assetFile.exists();
|
||||||
|
if (!success) {
|
||||||
|
File tmpFile = new File(assetFile.getParent(), assetFile.getName() + ".tmp");
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream stream = new FileOutputStream(tmpFile);
|
||||||
|
stream.write(asset.data);
|
||||||
|
stream.close();
|
||||||
|
success = tmpFile.renameTo(assetFile);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (success) {
|
||||||
|
Log.d(TAG, "Successfully created asset file " + assetFile);
|
||||||
|
return Asset.createFromRef(digest);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Failed creating asset file " + assetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createAssetFile(String digest) {
|
||||||
|
File dir = new File(new File(context.getFilesDir(), "assets"), digest.substring(digest.length() - 2));
|
||||||
|
dir.mkdirs();
|
||||||
|
return new File(dir, digest + ".asset");
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createAssetReceiveTempFile(String name) {
|
||||||
|
File dir = new File(context.getFilesDir(), "piece");
|
||||||
|
dir.mkdirs();
|
||||||
|
return new File(dir, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateDigest(byte[] data) {
|
||||||
|
try {
|
||||||
|
return Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(data), Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized ConnectionConfiguration[] getConfigurations() {
|
||||||
|
if (configurations == null) {
|
||||||
|
configurations = configDatabase.getAllConfigurations();
|
||||||
|
}
|
||||||
|
if (configurationsUpdated) {
|
||||||
|
configurationsUpdated = false;
|
||||||
|
ConnectionConfiguration[] newConfigurations = configDatabase.getAllConfigurations();
|
||||||
|
for (ConnectionConfiguration configuration : configurations) {
|
||||||
|
for (ConnectionConfiguration newConfiguration : newConfigurations) {
|
||||||
|
if (newConfiguration.name.equals(configuration.name)) {
|
||||||
|
newConfiguration.connected = configuration.connected;
|
||||||
|
newConfiguration.peerNodeId = configuration.peerNodeId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configurations = newConfigurations;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Configurations reported: " + Arrays.toString(configurations));
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addConnectedNode(Node node) {
|
||||||
|
connectedNodes.add(node);
|
||||||
|
onConnectedNodes(getConnectedNodesParcelableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeConnectedNode(String nodeId) {
|
||||||
|
for (Node connectedNode : new ArrayList<Node>(connectedNodes)) {
|
||||||
|
if (connectedNode.getId().equals(nodeId))
|
||||||
|
connectedNodes.remove(connectedNode);
|
||||||
|
}
|
||||||
|
onConnectedNodes(getConnectedNodesParcelableList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void syncToPeer(WearableConnection connection, String nodeId, long seqId) {
|
||||||
|
Log.d(TAG, "-- Start syncing over " + connection + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
||||||
|
Cursor cursor = nodeDatabase.getModifiedDataItems(nodeId, seqId, true);
|
||||||
|
if (cursor != null) {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
if (!syncRecordToPeer(connection, DataItemRecord.fromCursor(cursor))) break;
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
Log.d(TAG, "-- Done syncing over " + connection + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void syncRecordToAll(DataItemRecord record) {
|
||||||
|
Log.d(TAG, "Syncing record " + record + " over " + activeConnections.size() + " connections.");
|
||||||
|
for (WearableConnection connection : new ArrayList<WearableConnection>(activeConnections)) {
|
||||||
|
if (!syncRecordToPeer(connection, record)) {
|
||||||
|
Log.d(TAG, "Removing connection as it seems not usable: " + connection);
|
||||||
|
activeConnections.remove(connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean syncRecordToPeer(WearableConnection connection, DataItemRecord record) {
|
||||||
|
for (Asset asset : record.dataItem.getAssets().values()) {
|
||||||
|
syncAssetToPeer(connection, record, asset);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Sync over " + connection + ": " + record);
|
||||||
|
|
||||||
|
try {
|
||||||
|
connection.writeMessage(new RootMessage.Builder().setDataItem(record.toSetDataItem()).build());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncAssetToPeer(WearableConnection connection, DataItemRecord record, Asset asset) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "Sync over " + connection + ": " + asset);
|
||||||
|
RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
|
||||||
|
.digest(asset.getDigest())
|
||||||
|
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
||||||
|
.build()).unknown13(true).build();
|
||||||
|
connection.writeMessage(announceMessage);
|
||||||
|
File assetFile = createAssetFile(asset.getDigest());
|
||||||
|
String fileName = calculateDigest(announceMessage.toByteArray());
|
||||||
|
FileInputStream fis = new FileInputStream(assetFile);
|
||||||
|
byte[] arr = new byte[12215];
|
||||||
|
ByteString lastPiece = null;
|
||||||
|
int c = 0;
|
||||||
|
while ((c = fis.read(arr)) > 0) {
|
||||||
|
if (lastPiece != null) {
|
||||||
|
Log.d(TAG, "Sync over " + connection + ": Asset piece for fileName " + fileName + ": " + lastPiece);
|
||||||
|
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
|
||||||
|
}
|
||||||
|
lastPiece = ByteString.of(arr, 0, c);
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Sync over " + connection + ": Last asset piece for fileName " + fileName + ": " + lastPiece);
|
||||||
|
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
|
||||||
|
nodeDatabase.putAsset(asset, false);
|
||||||
|
for (AppKey appKey : appKeys) {
|
||||||
|
nodeDatabase.allowAssetAccess(asset.getDigest(), appKey.packageName, appKey.signatureDigest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCurrentSeqId(String nodeId) {
|
||||||
|
return nodeDatabase.getCurrentSeqId(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleFilePiece(WearableConnection connection, String fileName, byte[] bytes, String finalPieceDigest) {
|
||||||
|
File file = createAssetReceiveTempFile(fileName);
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(file, true);
|
||||||
|
fos.write(bytes);
|
||||||
|
fos.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
if (finalPieceDigest != null) {
|
||||||
|
// This is a final piece. If digest matches we're so happy!
|
||||||
|
try {
|
||||||
|
String digest = calculateDigest(Utils.readStreamToEnd(new FileInputStream(file)));
|
||||||
|
if (digest.equals(finalPieceDigest)) {
|
||||||
|
if (file.renameTo(createAssetFile(digest))) {
|
||||||
|
// TODO: Mark as stored in db
|
||||||
|
connection.writeMessage(new RootMessage.Builder().ackAsset(new AckAsset(digest)).build());
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Could not rename to target file name. delete=" + file.delete());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Received digest does not match. delete=" + file.delete());
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w(TAG, "Failed working with temp file. delete=" + file.delete(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onConnectReceived(WearableConnection connection, String nodeId, Connect connect) {
|
||||||
|
for (ConnectionConfiguration config : getConfigurations()) {
|
||||||
|
if (config.nodeId.equals(nodeId)) {
|
||||||
|
config.peerNodeId = connect.id;
|
||||||
|
config.connected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "Adding connection to list of open connections: " + connection);
|
||||||
|
activeConnections.add(connection);
|
||||||
|
onPeerConnected(new NodeParcelable(connect.id, connect.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<NodeParcelable> getConnectedNodesParcelableList() {
|
||||||
|
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
|
||||||
|
for (Node connectedNode : connectedNodes) {
|
||||||
|
nodes.add(new NodeParcelable(connectedNode));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWearableListener getAllListeners() {
|
||||||
|
return MultiListenerProxy.get(IWearableListener.class, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPeerConnected(NodeParcelable node) {
|
||||||
|
Log.d(TAG, "onPeerConnected: " + node);
|
||||||
|
try {
|
||||||
|
getAllListeners().onPeerConnected(node);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
addConnectedNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onConnectedNodes(List<NodeParcelable> nodes) {
|
||||||
|
Log.d(TAG, "onConnectedNodes: " + nodes);
|
||||||
|
try {
|
||||||
|
getAllListeners().onConnectedNodes(nodes);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataItemRecord putData(PutDataRequest request, String packageName) {
|
||||||
|
String host = request.getUri().getHost();
|
||||||
|
if (TextUtils.isEmpty(host)) host = getLocalNodeId();
|
||||||
|
DataItemInternal dataItem = new DataItemInternal(host, request.getUri().getPath());
|
||||||
|
for (Map.Entry<String, Asset> assetEntry : request.getAssets().entrySet()) {
|
||||||
|
Asset asset = prepareAsset(packageName, assetEntry.getValue());
|
||||||
|
if (asset != null) {
|
||||||
|
nodeDatabase.putAsset(asset, true);
|
||||||
|
dataItem.addAsset(assetEntry.getKey(), asset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataItem.data = request.getData();
|
||||||
|
DataItemRecord record = putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName), getLocalNodeId(), dataItem);
|
||||||
|
syncRecordToAll(record);
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataHolder getDataItems(String packageName) {
|
||||||
|
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolder(packageName, PackageUtils.firstSignatureDigest(context, packageName));
|
||||||
|
while (dataHolderItems.moveToNext()) {
|
||||||
|
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
||||||
|
}
|
||||||
|
dataHolderItems.moveToFirst();
|
||||||
|
dataHolderItems.moveToPrevious();
|
||||||
|
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataHolder getDataItemsByUri(Uri uri, String packageName) {
|
||||||
|
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
||||||
|
while (dataHolderItems.moveToNext()) {
|
||||||
|
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
||||||
|
}
|
||||||
|
dataHolderItems.moveToFirst();
|
||||||
|
dataHolderItems.moveToPrevious();
|
||||||
|
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(IWearableListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(IWearableListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableConnection(String name) {
|
||||||
|
configDatabase.setEnabledState(name, true);
|
||||||
|
configurationsUpdated = true;
|
||||||
|
if (name.equals("server") && nct == null) {
|
||||||
|
(nct = new NetworkConnectionThread(this, configDatabase.getConfiguration(name))).start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableConnection(String name) {
|
||||||
|
configDatabase.setEnabledState(name, false);
|
||||||
|
configurationsUpdated = true;
|
||||||
|
if (name.equals("server") && nct != null) {
|
||||||
|
activeConnections.remove(nct.getWearableConnection());
|
||||||
|
nct.close();
|
||||||
|
nct.interrupt();
|
||||||
|
nct = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteConnection(String name) {
|
||||||
|
configDatabase.deleteConfiguration(name);
|
||||||
|
configurationsUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createConnection(ConnectionConfiguration config) {
|
||||||
|
if (config.nodeId == null) config.nodeId = getLocalNodeId();
|
||||||
|
Log.d(TAG, "putConfig[nyp]: " + config);
|
||||||
|
configDatabase.putConfiguration(config);
|
||||||
|
configurationsUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int deleteDataItems(Uri uri, String packageName) {
|
||||||
|
return nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessageReceived(MessageEventParcelable messageEvent) {
|
||||||
|
Log.d(TAG, "onMessageReceived: " + messageEvent);
|
||||||
|
try {
|
||||||
|
getAllListeners().onMessageReceived(messageEvent);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataItemRecord getDataItemByUri(Uri uri, String packageName) {
|
||||||
|
Cursor cursor = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
||||||
|
DataItemRecord record = null;
|
||||||
|
if (cursor != null) {
|
||||||
|
if (cursor.moveToNext()) {
|
||||||
|
record = DataItemRecord.fromCursor(cursor);
|
||||||
|
}
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.microg.gms.wearable;
|
package org.microg.gms.wearable;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
@ -23,28 +24,29 @@ import com.google.android.gms.common.internal.GetServiceRequest;
|
||||||
import com.google.android.gms.common.internal.IGmsCallbacks;
|
import com.google.android.gms.common.internal.IGmsCallbacks;
|
||||||
|
|
||||||
import org.microg.gms.BaseService;
|
import org.microg.gms.BaseService;
|
||||||
|
import org.microg.gms.common.GmsService;
|
||||||
import org.microg.gms.common.PackageUtils;
|
import org.microg.gms.common.PackageUtils;
|
||||||
import org.microg.gms.common.Services;
|
|
||||||
|
|
||||||
public class WearableService extends BaseService {
|
public class WearableService extends BaseService {
|
||||||
|
|
||||||
private ConfigurationDatabaseHelper configurationDatabaseHelper;
|
private static WearableImpl wearable;
|
||||||
private NodeDatabaseHelper nodeDatabaseHelper;
|
|
||||||
|
|
||||||
public WearableService() {
|
public WearableService() {
|
||||||
super("GmsWearSvc", Services.WEARABLE.SERVICE_ID);
|
super("GmsWearSvc", GmsService.WEARABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized static WearableImpl getWearable(Context appCtx) {
|
||||||
|
if (wearable == null) {
|
||||||
|
ConfigurationDatabaseHelper configurationDatabaseHelper = new ConfigurationDatabaseHelper(appCtx);
|
||||||
|
NodeDatabaseHelper nodeDatabaseHelper = new NodeDatabaseHelper(appCtx);
|
||||||
|
wearable = new WearableImpl(appCtx, nodeDatabaseHelper, configurationDatabaseHelper);
|
||||||
|
}
|
||||||
|
return wearable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
|
||||||
super.onCreate();
|
|
||||||
configurationDatabaseHelper = new ConfigurationDatabaseHelper(this);
|
|
||||||
nodeDatabaseHelper = new NodeDatabaseHelper(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
|
|
||||||
PackageUtils.checkPackageUid(this, request.packageName, Binder.getCallingUid());
|
PackageUtils.checkPackageUid(this, request.packageName, Binder.getCallingUid());
|
||||||
callback.onPostInitComplete(0, new WearableServiceImpl(this, nodeDatabaseHelper, configurationDatabaseHelper, request.packageName), null);
|
callback.onPostInitComplete(0, new WearableServiceImpl(this, getWearable(getApplicationContext()), request.packageName), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,26 +17,14 @@
|
||||||
package org.microg.gms.wearable;
|
package org.microg.gms.wearable;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.android.gms.common.api.Status;
|
import com.google.android.gms.common.api.Status;
|
||||||
import com.google.android.gms.common.data.DataHolder;
|
|
||||||
import com.google.android.gms.wearable.Asset;
|
|
||||||
import com.google.android.gms.wearable.ConnectionConfiguration;
|
import com.google.android.gms.wearable.ConnectionConfiguration;
|
||||||
import com.google.android.gms.wearable.Node;
|
|
||||||
import com.google.android.gms.wearable.internal.AddListenerRequest;
|
import com.google.android.gms.wearable.internal.AddListenerRequest;
|
||||||
import com.google.android.gms.wearable.internal.AmsEntityUpdateParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.CapabilityInfoParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.ChannelEventParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.DataItemParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.DeleteDataItemsResponse;
|
import com.google.android.gms.wearable.internal.DeleteDataItemsResponse;
|
||||||
import com.google.android.gms.wearable.internal.GetConfigResponse;
|
import com.google.android.gms.wearable.internal.GetConfigResponse;
|
||||||
import com.google.android.gms.wearable.internal.GetConfigsResponse;
|
import com.google.android.gms.wearable.internal.GetConfigsResponse;
|
||||||
|
@ -44,219 +32,61 @@ import com.google.android.gms.wearable.internal.GetConnectedNodesResponse;
|
||||||
import com.google.android.gms.wearable.internal.GetDataItemResponse;
|
import com.google.android.gms.wearable.internal.GetDataItemResponse;
|
||||||
import com.google.android.gms.wearable.internal.GetLocalNodeResponse;
|
import com.google.android.gms.wearable.internal.GetLocalNodeResponse;
|
||||||
import com.google.android.gms.wearable.internal.IWearableCallbacks;
|
import com.google.android.gms.wearable.internal.IWearableCallbacks;
|
||||||
import com.google.android.gms.wearable.internal.IWearableListener;
|
|
||||||
import com.google.android.gms.wearable.internal.IWearableService;
|
import com.google.android.gms.wearable.internal.IWearableService;
|
||||||
import com.google.android.gms.wearable.internal.MessageEventParcelable;
|
|
||||||
import com.google.android.gms.wearable.internal.NodeParcelable;
|
import com.google.android.gms.wearable.internal.NodeParcelable;
|
||||||
import com.google.android.gms.wearable.internal.PutDataRequest;
|
import com.google.android.gms.wearable.internal.PutDataRequest;
|
||||||
import com.google.android.gms.wearable.internal.PutDataResponse;
|
import com.google.android.gms.wearable.internal.PutDataResponse;
|
||||||
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
|
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
|
||||||
|
|
||||||
import org.microg.gms.common.PackageUtils;
|
public class WearableServiceImpl extends IWearableService.Stub {
|
||||||
import org.microg.gms.common.Utils;
|
|
||||||
import org.microg.wearable.WearableConnection;
|
|
||||||
import org.microg.wearable.proto.AckAsset;
|
|
||||||
import org.microg.wearable.proto.AppKey;
|
|
||||||
import org.microg.wearable.proto.AppKeys;
|
|
||||||
import org.microg.wearable.proto.FilePiece;
|
|
||||||
import org.microg.wearable.proto.RootMessage;
|
|
||||||
import org.microg.wearable.proto.SetAsset;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import okio.ByteString;
|
|
||||||
|
|
||||||
public class WearableServiceImpl extends IWearableService.Stub implements IWearableListener {
|
|
||||||
private static final String TAG = "GmsWearSvcImpl";
|
private static final String TAG = "GmsWearSvcImpl";
|
||||||
|
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCES = "cw_node";
|
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCE_NODE_ID = "node_id";
|
|
||||||
private static final String CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK = "nextSeqIdBlock";
|
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final String packageName;
|
private final String packageName;
|
||||||
private final NodeDatabaseHelper nodeDatabase;
|
private final WearableImpl wearable;
|
||||||
private final ConfigurationDatabaseHelper configDatabase;
|
|
||||||
private Set<IWearableListener> listeners = new HashSet<IWearableListener>();
|
|
||||||
private Set<Node> connectedNodes = new HashSet<Node>();
|
|
||||||
private ConnectionConfiguration[] configurations;
|
|
||||||
private boolean configurationsUpdated = false;
|
|
||||||
private NetworkConnectionThread nct;
|
|
||||||
|
|
||||||
private long seqIdBlock;
|
public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) {
|
||||||
private long seqIdInBlock = -1;
|
|
||||||
|
|
||||||
public WearableServiceImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase, String packageName) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.nodeDatabase = nodeDatabase;
|
this.wearable = wearable;
|
||||||
this.configDatabase = configDatabase;
|
|
||||||
this.packageName = packageName;
|
this.packageName = packageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLocalNodeId() {
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
|
||||||
String nodeId = preferences.getString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, null);
|
|
||||||
if (nodeId == null) {
|
|
||||||
nodeId = UUID.randomUUID().toString();
|
|
||||||
preferences.edit().putString(CLOCKWORK_NODE_PREFERENCE_NODE_ID, nodeId).apply();
|
|
||||||
}
|
|
||||||
return nodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized long getNextSeqId() {
|
|
||||||
SharedPreferences preferences = context.getSharedPreferences(CLOCKWORK_NODE_PREFERENCES, Context.MODE_PRIVATE);
|
|
||||||
if (seqIdInBlock < 0) seqIdInBlock = 1000;
|
|
||||||
if (seqIdInBlock >= 1000) {
|
|
||||||
seqIdBlock = preferences.getLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, 100);
|
|
||||||
preferences.edit().putLong(CLOCKWORK_NODE_PREFERENCE_NEXT_SEQ_ID_BLOCK, seqIdBlock + seqIdInBlock).apply();
|
|
||||||
seqIdInBlock = 0;
|
|
||||||
}
|
|
||||||
return seqIdBlock + seqIdInBlock++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putData(IWearableCallbacks callbacks, PutDataRequest request) throws RemoteException {
|
public void putData(IWearableCallbacks callbacks, PutDataRequest request) throws RemoteException {
|
||||||
Log.d(TAG, "putData: " + request.toString(true));
|
Log.d(TAG, "putData: " + request.toString(true));
|
||||||
String host = request.getUri().getHost();
|
DataItemRecord record = wearable.putData(request, packageName);
|
||||||
if (TextUtils.isEmpty(host)) host = getLocalNodeId();
|
callbacks.onPutDataResponse(new PutDataResponse(0, record.toParcelable()));
|
||||||
DataItemInternal dataItem = new DataItemInternal(host, request.getUri().getPath());
|
|
||||||
for (Map.Entry<String, Asset> assetEntry : request.getAssets().entrySet()) {
|
|
||||||
Asset asset = prepareAsset(packageName, assetEntry.getValue());
|
|
||||||
if (asset != null) {
|
|
||||||
nodeDatabase.putAsset(asset, true);
|
|
||||||
dataItem.addAsset(assetEntry.getKey(), asset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataItem.data = request.getData();
|
|
||||||
DataItemParcelable parcelable = putDataItem(packageName, PackageUtils.firstSignatureDigest(context, packageName),
|
|
||||||
getLocalNodeId(), dataItem).toParcelable();
|
|
||||||
callbacks.onPutDataResponse(new PutDataResponse(0, parcelable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataItemRecord putDataItem(String packageName, String signatureDigest, String source, DataItemInternal dataItem) {
|
|
||||||
DataItemRecord record = new DataItemRecord();
|
|
||||||
record.packageName = packageName;
|
|
||||||
record.signatureDigest = signatureDigest;
|
|
||||||
record.deleted = false;
|
|
||||||
record.source = source;
|
|
||||||
record.dataItem = dataItem;
|
|
||||||
record.v1SeqId = getNextSeqId();
|
|
||||||
if (record.source.equals(getLocalNodeId())) record.seqId = record.v1SeqId;
|
|
||||||
return putDataItem(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataItemRecord putDataItem(DataItemRecord record) {
|
|
||||||
nodeDatabase.putRecord(record);
|
|
||||||
return record;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Asset prepareAsset(String packageName, Asset asset) {
|
|
||||||
if (asset.getFd() != null && asset.data == null) {
|
|
||||||
try {
|
|
||||||
asset.data = Utils.readStreamToEnd(new FileInputStream(asset.getFd().getFileDescriptor()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (asset.data != null) {
|
|
||||||
String digest = calculateDigest(asset.data);
|
|
||||||
File assetFile = createAssetFile(digest);
|
|
||||||
boolean success = assetFile.exists();
|
|
||||||
if (!success) {
|
|
||||||
File tmpFile = new File(assetFile.getParent(), assetFile.getName() + ".tmp");
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileOutputStream stream = new FileOutputStream(tmpFile);
|
|
||||||
stream.write(asset.data);
|
|
||||||
stream.close();
|
|
||||||
success = tmpFile.renameTo(assetFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
Log.d(TAG, "Successfully created asset file " + assetFile);
|
|
||||||
return Asset.createFromRef(digest);
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Failed creating asset file " + assetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createAssetFile(String digest) {
|
|
||||||
File dir = new File(new File(context.getFilesDir(), "assets"), digest.substring(digest.length() - 2));
|
|
||||||
dir.mkdirs();
|
|
||||||
return new File(dir, digest + ".asset");
|
|
||||||
}
|
|
||||||
|
|
||||||
private File createAssetReceiveTempFile(String name) {
|
|
||||||
File dir = new File(context.getFilesDir(), "piece");
|
|
||||||
dir.mkdirs();
|
|
||||||
return new File(dir, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String calculateDigest(byte[] data) {
|
|
||||||
try {
|
|
||||||
return Base64.encodeToString(MessageDigest.getInstance("SHA1").digest(data), Base64.NO_WRAP | Base64.NO_PADDING | Base64.URL_SAFE);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getDataItem(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
public void getDataItem(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
||||||
Log.d(TAG, "getDataItem: " + uri);
|
Log.d(TAG, "getDataItem: " + uri);
|
||||||
Cursor cursor = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
|
||||||
if (cursor != null) {
|
DataItemRecord record = wearable.getDataItemByUri(uri, packageName);
|
||||||
if (cursor.moveToNext()) {
|
if (record != null) {
|
||||||
DataItemParcelable dataItem = new DataItemParcelable(new Uri.Builder().scheme("wear").authority(cursor.getString(0)).path(cursor.getString(1)).build());
|
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, record.toParcelable()));
|
||||||
dataItem.data = cursor.getBlob(2);
|
} else {
|
||||||
Log.d(TAG, "getDataItem.asset " + cursor.getString(5));
|
|
||||||
// TODO: assets
|
|
||||||
callbacks.onGetDataItemResponse(new GetDataItemResponse(0, dataItem));
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
// TODO: negative
|
// TODO: negative
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getDataItems(IWearableCallbacks callbacks) throws RemoteException {
|
public void getDataItems(IWearableCallbacks callbacks) throws RemoteException {
|
||||||
Log.d(TAG, "getDataItems: " + callbacks);
|
Log.d(TAG, "getDataItems: " + callbacks);
|
||||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolder(packageName, PackageUtils.firstSignatureDigest(context, packageName));
|
callbacks.onDataHolder(wearable.getDataItems(packageName));
|
||||||
while (dataHolderItems.moveToNext()) {
|
|
||||||
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
|
||||||
}
|
|
||||||
dataHolderItems.moveToFirst();
|
|
||||||
dataHolderItems.moveToPrevious();
|
|
||||||
callbacks.onDataHolder(DataHolder.fromCursor(dataHolderItems, 0, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri, int i) throws RemoteException {
|
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri, int i) throws RemoteException {
|
||||||
Log.d(TAG, "getDataItemsByUri: " + uri);
|
Log.d(TAG, "getDataItemsByUri: " + uri);
|
||||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
callbacks.onDataHolder(wearable.getDataItemsByUri(uri, packageName));
|
||||||
callbacks.onDataHolder(DataHolder.fromCursor(dataHolderItems, 0, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteDataItems(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
public void deleteDataItems(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
|
||||||
Log.d(TAG, "deleteDataItems: " + uri);
|
Log.d(TAG, "deleteDataItems: " + uri);
|
||||||
int count = nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
|
callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName)));
|
||||||
callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, count));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -267,7 +97,7 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
||||||
@Override
|
@Override
|
||||||
public void getLocalNode(IWearableCallbacks callbacks) throws RemoteException {
|
public void getLocalNode(IWearableCallbacks callbacks) throws RemoteException {
|
||||||
try {
|
try {
|
||||||
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(getLocalNodeId(), getLocalNodeId())));
|
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(wearable.getLocalNodeId(), wearable.getLocalNodeId())));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(8, null));
|
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(8, null));
|
||||||
}
|
}
|
||||||
|
@ -276,51 +106,41 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
||||||
@Override
|
@Override
|
||||||
public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteException {
|
public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteException {
|
||||||
Log.d(TAG, "getConnectedNodes");
|
Log.d(TAG, "getConnectedNodes");
|
||||||
callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, getConnectedNodesParcelableList()));
|
callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, wearable.getConnectedNodesParcelableList()));
|
||||||
}
|
|
||||||
|
|
||||||
private List<NodeParcelable> getConnectedNodesParcelableList() {
|
|
||||||
List<NodeParcelable> nodes = new ArrayList<NodeParcelable>();
|
|
||||||
for (Node connectedNode : connectedNodes) {
|
|
||||||
nodes.add(new NodeParcelable(connectedNode));
|
|
||||||
}
|
|
||||||
return nodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
|
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
|
||||||
Log.d(TAG, "addListener[nyp]: " + request);
|
Log.d(TAG, "addListener[nyp]: " + request);
|
||||||
listeners.add(request.listener);
|
if (request.listener != null) {
|
||||||
|
wearable.addListener(request.listener);
|
||||||
|
}
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest request) throws RemoteException {
|
public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest request) throws RemoteException {
|
||||||
Log.d(TAG, "removeListener[nyp]: " + request);
|
Log.d(TAG, "removeListener[nyp]: " + request);
|
||||||
listeners.remove(request.listener);
|
wearable.removeListener(request.listener);
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putConfig(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException {
|
public void putConfig(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException {
|
||||||
if (config.nodeId == null) config.nodeId = getLocalNodeId();
|
wearable.createConnection(config);
|
||||||
Log.d(TAG, "putConfig[nyp]: " + config);
|
|
||||||
configDatabase.putConfiguration(config);
|
|
||||||
configurationsUpdated = true;
|
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteConfig(IWearableCallbacks callbacks, String name) throws RemoteException {
|
public void deleteConfig(IWearableCallbacks callbacks, String name) throws RemoteException {
|
||||||
configDatabase.deleteConfiguration(name);
|
wearable.deleteConnection(name);
|
||||||
configurationsUpdated = true;
|
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getConfig(IWearableCallbacks callbacks) throws RemoteException {
|
public void getConfig(IWearableCallbacks callbacks) throws RemoteException {
|
||||||
Log.d(TAG, "getConfig");
|
Log.d(TAG, "getConfig");
|
||||||
ConnectionConfiguration[] configurations = getConfigurations();
|
ConnectionConfiguration[] configurations = wearable.getConfigurations();
|
||||||
if (configurations == null || configurations.length == 0) {
|
if (configurations == null || configurations.length == 0) {
|
||||||
callbacks.onGetConfigResponse(new GetConfigResponse(1, new ConnectionConfiguration(null, null, 0, 0, false)));
|
callbacks.onGetConfigResponse(new GetConfigResponse(1, new ConnectionConfiguration(null, null, 0, 0, false)));
|
||||||
} else {
|
} else {
|
||||||
|
@ -332,59 +152,25 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
||||||
public void getConfigs(IWearableCallbacks callbacks) throws RemoteException {
|
public void getConfigs(IWearableCallbacks callbacks) throws RemoteException {
|
||||||
Log.d(TAG, "getConfigs");
|
Log.d(TAG, "getConfigs");
|
||||||
try {
|
try {
|
||||||
callbacks.onGetConfigsResponse(new GetConfigsResponse(0, getConfigurations()));
|
callbacks.onGetConfigsResponse(new GetConfigsResponse(0, wearable.getConfigurations()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
callbacks.onGetConfigsResponse(new GetConfigsResponse(8, new ConnectionConfiguration[0]));
|
callbacks.onGetConfigsResponse(new GetConfigsResponse(8, new ConnectionConfiguration[0]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized ConnectionConfiguration[] getConfigurations() {
|
|
||||||
if (configurations == null) {
|
|
||||||
configurations = configDatabase.getAllConfigurations();
|
|
||||||
}
|
|
||||||
if (configurationsUpdated) {
|
|
||||||
configurationsUpdated = false;
|
|
||||||
ConnectionConfiguration[] newConfigurations = configDatabase.getAllConfigurations();
|
|
||||||
for (ConnectionConfiguration configuration : configurations) {
|
|
||||||
for (ConnectionConfiguration newConfiguration : newConfigurations) {
|
|
||||||
if (newConfiguration.name.equals(configuration.name)) {
|
|
||||||
newConfiguration.connected = configuration.connected;
|
|
||||||
newConfiguration.peerNodeId = configuration.peerNodeId;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
configurations = newConfigurations;
|
|
||||||
}
|
|
||||||
return configurations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
|
public void enableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
|
||||||
Log.d(TAG, "enableConnection: " + name);
|
Log.d(TAG, "enableConnection: " + name);
|
||||||
configDatabase.setEnabledState(name, true);
|
wearable.enableConnection(name);
|
||||||
configurationsUpdated = true;
|
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
if (name.equals("server")) {
|
|
||||||
// TODO: hackady hack
|
|
||||||
(nct = new NetworkConnectionThread(this, configDatabase.getConfiguration(name))).start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
|
public void disableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
|
||||||
Log.d(TAG, "disableConnection: " + name);
|
Log.d(TAG, "disableConnection: " + name);
|
||||||
configDatabase.setEnabledState(name, false);
|
wearable.disableConnection(name);
|
||||||
configurationsUpdated = true;
|
|
||||||
callbacks.onStatus(Status.SUCCESS);
|
callbacks.onStatus(Status.SUCCESS);
|
||||||
if (name.equals("server")) {
|
|
||||||
// TODO: hacady hack
|
|
||||||
if (nct != null) {
|
|
||||||
nct.close();
|
|
||||||
nct.interrupt();
|
|
||||||
nct = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -393,195 +179,4 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
|
||||||
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
|
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDataChanged(DataHolder data) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onDataChanged(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessageReceived(MessageEventParcelable messageEvent) {
|
|
||||||
Log.d(TAG, "onMessageReceived: " + messageEvent);
|
|
||||||
for (IWearableListener listener : new ArrayList<IWearableListener>(listeners)) {
|
|
||||||
try {
|
|
||||||
listener.onMessageReceived(messageEvent);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerConnected(NodeParcelable node) {
|
|
||||||
Log.d(TAG, "onPeerConnected: " + node);
|
|
||||||
for (IWearableListener listener : new ArrayList<IWearableListener>(listeners)) {
|
|
||||||
try {
|
|
||||||
listener.onPeerConnected(node);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addConnectedNode(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addConnectedNode(Node node) {
|
|
||||||
connectedNodes.add(node);
|
|
||||||
onConnectedNodes(getConnectedNodesParcelableList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeConnectedNode(String nodeId) {
|
|
||||||
Node toRemove = null;
|
|
||||||
for (Node node : connectedNodes) {
|
|
||||||
if (node.getId().equals(nodeId)) {
|
|
||||||
toRemove = node;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
connectedNodes.remove(toRemove);
|
|
||||||
onConnectedNodes(getConnectedNodesParcelableList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerDisconnected(NodeParcelable node) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onPeerDisconnected(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectedNodes(List<NodeParcelable> nodes) {
|
|
||||||
Log.d(TAG, "onConnectedNodes: " + nodes);
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
try {
|
|
||||||
listener.onConnectedNodes(nodes);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNotificationReceived(AncsNotificationParcelable notification) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onNotificationReceived(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChannelEvent(ChannelEventParcelable channelEvent) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onChannelEvent(channelEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectedCapabilityChanged(CapabilityInfoParcelable capabilityInfo) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onConnectedCapabilityChanged(capabilityInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEntityUpdate(AmsEntityUpdateParcelable update) throws RemoteException {
|
|
||||||
for (IWearableListener listener : listeners) {
|
|
||||||
listener.onEntityUpdate(update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void syncToPeer(WearableConnection connection, String nodeId, long seqId) {
|
|
||||||
Log.d(TAG, "-- Start syncing over " + connection + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
|
||||||
Cursor cursor = nodeDatabase.getModifiedDataItems(nodeId, seqId, true);
|
|
||||||
if (cursor != null) {
|
|
||||||
while (cursor.moveToNext()) {
|
|
||||||
DataItemRecord record = DataItemRecord.fromCursor(cursor);
|
|
||||||
for (Asset asset : record.dataItem.getAssets().values()) {
|
|
||||||
syncAssetToPeer(connection, record, asset);
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Sync over " + connection + ": " + record);
|
|
||||||
|
|
||||||
try {
|
|
||||||
connection.writeMessage(new RootMessage.Builder().setDataItem(record.toSetDataItem()).build());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
Log.d(TAG, "-- Done syncing over " + connection + ", nodeId " + nodeId + " starting with seqId " + seqId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncAssetToPeer(WearableConnection connection, DataItemRecord record, Asset asset) {
|
|
||||||
try {
|
|
||||||
Log.d(TAG, "Sync over " + connection + ": " + asset);
|
|
||||||
connection.writeMessage(new RootMessage.Builder().setAsset(
|
|
||||||
new SetAsset.Builder()
|
|
||||||
.digest(asset.getDigest())
|
|
||||||
.appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
|
|
||||||
.build()).unknown13(true).build());
|
|
||||||
File assetFile = createAssetFile(asset.getDigest());
|
|
||||||
String fileName = calculateDigest(assetFile.toString().getBytes());
|
|
||||||
FileInputStream fis = new FileInputStream(assetFile);
|
|
||||||
byte[] arr = new byte[12215];
|
|
||||||
ByteString lastPiece = null;
|
|
||||||
int c = 0;
|
|
||||||
while ((c = fis.read(arr)) > 0) {
|
|
||||||
if (lastPiece != null) {
|
|
||||||
Log.d(TAG, "Sync over " + connection + ": Asset piece for fileName " + fileName + ": " + lastPiece);
|
|
||||||
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
|
|
||||||
}
|
|
||||||
lastPiece = ByteString.of(arr, 0, c);
|
|
||||||
}
|
|
||||||
Log.d(TAG, "Sync over " + connection + ": Last asset piece for fileName " + fileName + ": " + lastPiece);
|
|
||||||
connection.writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
|
|
||||||
nodeDatabase.putAsset(asset, false);
|
|
||||||
for (AppKey appKey : appKeys) {
|
|
||||||
nodeDatabase.allowAssetAccess(asset.getDigest(), appKey.packageName, appKey.signatureDigest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCurrentSeqId(String nodeId) {
|
|
||||||
return nodeDatabase.getCurrentSeqId(nodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleFilePiece(WearableConnection connection, String fileName, byte[] bytes, String finalPieceDigest) {
|
|
||||||
File file = createAssetReceiveTempFile(fileName);
|
|
||||||
try {
|
|
||||||
FileOutputStream fos = new FileOutputStream(file, true);
|
|
||||||
fos.write(bytes);
|
|
||||||
fos.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
if (finalPieceDigest != null) {
|
|
||||||
// This is a final piece. If digest matches we're so happy!
|
|
||||||
try {
|
|
||||||
String digest = calculateDigest(Utils.readStreamToEnd(new FileInputStream(file)));
|
|
||||||
if (digest.equals(finalPieceDigest)) {
|
|
||||||
if (file.renameTo(createAssetFile(digest))) {
|
|
||||||
// TODO: Mark as stored in db
|
|
||||||
connection.writeMessage(new RootMessage.Builder().ackAsset(new AckAsset(digest)).build());
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Could not rename to target file name. delete=" + file.delete());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Received digest does not match. delete=" + file.delete());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, "Failed working with temp file. delete=" + file.delete(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,25 +17,25 @@
|
||||||
-dontnote
|
-dontnote
|
||||||
|
|
||||||
# Keep dynamically loaded GMS classes
|
# Keep dynamically loaded GMS classes
|
||||||
-keep public class com.google.android.gms.maps.internal.CreatorImpl
|
-keep public class com.google.android.gms.maps.internal.CreatorImpl { public *; }
|
||||||
-keep public class com.google.android.gms.common.security.ProviderInstallerImpl
|
-keep public class com.google.android.gms.common.security.ProviderInstallerImpl { public *; }
|
||||||
-keep public class com.google.android.gms.plus.plusone.PlusOneButtonCreatorImpl
|
-keep public class com.google.android.gms.plus.plusone.PlusOneButtonCreatorImpl { public *; }
|
||||||
|
|
||||||
-keepclassmembers class com.google.android.gms.common.security.ProviderInstallerImpl {
|
|
||||||
public *;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Keep AutoSafeParcelables
|
# Keep AutoSafeParcelables
|
||||||
-keep public class * extends org.microg.safeparcel.AutoSafeParcelable
|
-keep public class * extends org.microg.safeparcel.AutoSafeParcelable {
|
||||||
-keepclassmembers public class * extends org.microg.safeparcel.AutoSafeParcelable {
|
|
||||||
@org.microg.safeparcel.SafeParceled *;
|
@org.microg.safeparcel.SafeParceled *;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Keep asInterface method cause it's accessed from SafeParcel
|
||||||
|
-keepattributes InnerClasses
|
||||||
|
-keepclassmembers interface * extends android.os.IInterface {
|
||||||
|
public static class *;
|
||||||
|
}
|
||||||
|
-keep public class * extends android.os.Binder { public static *; }
|
||||||
|
|
||||||
# Keep library info
|
# Keep library info
|
||||||
-keep class **.BuildConfig
|
-keep class **.BuildConfig { *; }
|
||||||
-keepclassmembers class **.BuildConfig { *; }
|
|
||||||
|
|
||||||
# Keep protobuf class builders
|
# Keep protobuf class builders
|
||||||
-keep public class * extends com.squareup.wire.Message
|
-keep public class * extends com.squareup.wire.Message
|
||||||
-keep public class * extends com.squareup.wire.Message$Builder
|
-keep public class * extends com.squareup.wire.Message$Builder { public <init>(...); }
|
||||||
-keepclassmembers class * extends com.squareup.wire.Message$Builder { public <init>(...); }
|
|
||||||
|
|
Loading…
Reference in a new issue