diff --git a/extern/GmsApi b/extern/GmsApi
index 75562ce8..6aa11065 160000
--- a/extern/GmsApi
+++ b/extern/GmsApi
@@ -1 +1 @@
-Subproject commit 75562ce8b6d19fccde3ca46ab853513f48832a05
+Subproject commit 6aa110657beec0b3e6c26d1030943e0b97683335
diff --git a/extern/UnifiedNlp b/extern/UnifiedNlp
index d184e0e7..97b01d50 160000
--- a/extern/UnifiedNlp
+++ b/extern/UnifiedNlp
@@ -1 +1 @@
-Subproject commit d184e0e7f10aaad979019d3efe5fc10a68299849
+Subproject commit 97b01d50d4fc54061ff8495e92217f31454acd0a
diff --git a/extern/Wearable b/extern/Wearable
index c12fe119..5c24adaa 160000
--- a/extern/Wearable
+++ b/extern/Wearable
@@ -1 +1 @@
-Subproject commit c12fe119c8f71b4e7b458f480270f686dae34343
+Subproject commit 5c24adaa3928de68167ce31c0fa5b9b1a3256677
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index e8098c15..92583ac0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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
diff --git a/play-services-core/build.gradle b/play-services-core/build.gradle
index 3b2ea9b8..389d4168 100644
--- a/play-services-core/build.gradle
+++ b/play-services-core/build.gradle
@@ -19,7 +19,7 @@ buildscript {
jcenter()
}
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() {
def stdout = new ByteArrayOutputStream()
- exec {
- commandLine 'git', 'describe', '--tags', '--always', '--dirty'
- standardOutput = stdout
- }
- return stdout.toString().trim()
+ if (rootProject.file("gradlew").exists())
+ exec { commandLine 'git', 'describe', '--tags', '--always', '--dirty'; standardOutput = stdout }
+ else // automatic build system, don't tag dirty
+ exec { commandLine 'git', 'describe', '--tags', '--always'; standardOutput = stdout }
+ return stdout.toString().trim().substring(1)
}
int getMyVersionCode(String ref) {
diff --git a/play-services-core/src/main/AndroidManifest.xml b/play-services-core/src/main/AndroidManifest.xml
index 8346a7a0..1d20c4f8 100644
--- a/play-services-core/src/main/AndroidManifest.xml
+++ b/play-services-core/src/main/AndroidManifest.xml
@@ -79,7 +79,6 @@
-
supportedServiceIds;
+ private final EnumSet supportedServices;
- public AbstractGmsServiceBroker(Integer supportedServiceId, Integer... supportedServiceIds) {
- this(combine(supportedServiceId, supportedServiceIds));
- }
-
- private static Set combine(Integer i, Integer... is) {
- Set integers = new HashSet(Arrays.asList(is));
- integers.add(i);
- return integers;
- }
-
- public AbstractGmsServiceBroker(Set supportedServiceIds) {
- this.supportedServiceIds = supportedServiceIds;
+ public AbstractGmsServiceBroker(EnumSet supportedServices) {
+ this.supportedServices = supportedServices;
}
@Deprecated
@@ -62,21 +48,21 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
throws RemoteException {
Bundle extras = params == null ? new Bundle() : params;
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
@Override
public void getPanoramaService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.PANORAMA.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.PANORAMA, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getAppDataSearchService(IGmsCallbacks callback, int versionCode, String packageName)
throws RemoteException {
- callGetService(Services.INDEX.SERVICE_ID, callback, versionCode, packageName);
+ callGetService(GmsService.INDEX, callback, versionCode, packageName);
}
@Deprecated
@@ -89,28 +75,28 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
@Override
public void getPeopleService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.PEOPLE.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.PEOPLE, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getReportingService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.LOCATION_REPORTING.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.LOCATION_REPORTING, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getLocationService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.LOCATION.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.LOCATION, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getGoogleLocationManagerService(IGmsCallbacks callback, int versionCode,
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
@@ -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.desiredLocale", desiredLocale);
//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
@Override
public void getAppStateService(IGmsCallbacks callback, int versionCode, String packageName,
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
@Override
public void getPlayLogService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.PLAY_LOG.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.PLAY_LOG, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getAdMobService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.ADREQUEST.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.ADREQUEST, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getDroidGuardService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.DROIDGUARD.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.DROIDGUARD, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getLockboxService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.LOCKBOX.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.LOCKBOX, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getCastMirroringService(IGmsCallbacks callback, int versionCode, String packageName,
Bundle params) throws RemoteException {
- callGetService(Services.CAST_MIRRORING.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.CAST_MIRRORING, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getNetworkQualityService(IGmsCallbacks callback, int versionCode,
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
@Override
public void getGoogleIdentityService(IGmsCallbacks callback, int versionCode,
String packageName, Bundle params) throws RemoteException {
- callGetService(Services.ACCOUNT.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.ACCOUNT, callback, versionCode, packageName, params);
}
@Deprecated
@Override
public void getGoogleFeedbackService(IGmsCallbacks callback, int versionCode,
String packageName, Bundle params) throws RemoteException {
- callGetService(Services.FEEDBACK.SERVICE_ID, callback, versionCode, packageName, params);
+ callGetService(GmsService.FEEDBACK, callback, versionCode, packageName, params);
}
@Deprecated
@@ -200,55 +186,55 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
@Override
public void getDriveService(IGmsCallbacks callback, int versionCode, String packageName,
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
@Override
public void getLightweightAppDataSearchService(IGmsCallbacks callback, int versionCode,
String packageName) throws RemoteException {
- callGetService(Services.LIGHTWEIGHT_INDEX.SERVICE_ID, callback, versionCode, packageName);
+ callGetService(GmsService.LIGHTWEIGHT_INDEX, callback, versionCode, packageName);
}
@Deprecated
@Override
public void getSearchAdministrationService(IGmsCallbacks callback, int versionCode,
String packageName) throws RemoteException {
- callGetService(Services.SEARCH_ADMINISTRATION.SERVICE_ID, callback, versionCode, packageName);
+ callGetService(GmsService.SEARCH_ADMINISTRATION, callback, versionCode, packageName);
}
@Deprecated
@Override
public void getAutoBackupService(IGmsCallbacks callback, int versionCode, String packageName,
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
@Override
public void getAddressService(IGmsCallbacks callback, int versionCode, String packageName)
throws RemoteException {
- callGetService(Services.ADDRESS.SERVICE_ID, callback, versionCode, packageName);
+ callGetService(GmsService.ADDRESS, callback, versionCode, packageName);
}
@Deprecated
@Override
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 {
- 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 {
- 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 {
- GetServiceRequest request = new GetServiceRequest(serviceId);
+ private void callGetService(GmsService service, IGmsCallbacks callback, int gmsVersion, String packageName, Bundle extras, String accountName, String[] scopes) throws RemoteException {
+ GetServiceRequest request = new GetServiceRequest(service.SERVICE_ID);
request.gmsVersion = gmsVersion;
request.packageName = packageName;
request.extras = extras;
@@ -267,15 +253,16 @@ public abstract class AbstractGmsServiceBroker extends IGmsServiceBroker.Stub {
@Override
public void getService(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
- if (supportedServiceIds.contains(request.serviceId) || supportedServiceIds.contains(ID_ACCEPT_ALL)) {
- handleServiceRequest(callback, request);
+ GmsService gmsService = GmsService.byServiceId(request.serviceId);
+ if ((supportedServices.contains(gmsService)) || supportedServices.contains(GmsService.ANY)) {
+ handleServiceRequest(callback, request, gmsService);
} else {
Log.d(TAG, "Service not supported: " + request);
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
public void validateAccount(IGmsCallbacks callback, ValidateAccountRequest request) throws RemoteException {
diff --git a/play-services-core/src/main/java/org/microg/gms/BaseService.java b/play-services-core/src/main/java/org/microg/gms/BaseService.java
index 23958bc7..9ec3a902 100644
--- a/play-services-core/src/main/java/org/microg/gms/BaseService.java
+++ b/play-services-core/src/main/java/org/microg/gms/BaseService.java
@@ -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.IGmsServiceBroker;
+import org.microg.gms.common.GmsService;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+
public abstract class BaseService extends Service {
private final IGmsServiceBroker broker;
protected final String TAG;
- public BaseService(String tag, Integer supportedServiceId, Integer... supportedServiceIds) {
+ public BaseService(String tag, GmsService supportedService, GmsService... supportedServices) {
this.TAG = tag;
- broker = new AbstractGmsServiceBroker(supportedServiceId, supportedServiceIds) {
+ EnumSet services = EnumSet.of(supportedService);
+ services.addAll(Arrays.asList(supportedServices));
+ broker = new AbstractGmsServiceBroker(services) {
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
try {
request.extras.keySet(); // call to unparcel()
} catch (Exception e) {
// Sometimes we need to define the correct ClassLoader before unparcel(). Ignore those.
}
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();
}
- public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException;
+ public abstract void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException;
}
diff --git a/play-services-core/src/main/java/org/microg/gms/DummyService.java b/play-services-core/src/main/java/org/microg/gms/DummyService.java
index 7bec8072..b4a6f87e 100644
--- a/play-services-core/src/main/java/org/microg/gms/DummyService.java
+++ b/play-services-core/src/main/java/org/microg/gms/DummyService.java
@@ -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.IGmsCallbacks;
+import org.microg.gms.common.GmsService;
+
public class DummyService extends BaseService {
public DummyService() {
- super("GmsDummySvc", AbstractGmsServiceBroker.ID_ACCEPT_ALL);
+ super("GmsDummySvc", GmsService.ANY);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/ads/GService.java b/play-services-core/src/main/java/org/microg/gms/ads/GService.java
index 3604a4f9..77f934f0 100644
--- a/play-services-core/src/main/java/org/microg/gms/ads/GService.java
+++ b/play-services-core/src/main/java/org/microg/gms/ads/GService.java
@@ -20,16 +20,16 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class GService extends BaseService {
public GService() {
- super("GmsAdsGSvc", Services.GSERVICES.SERVICE_ID, Services.ADREQUEST.SERVICE_ID);
+ super("GmsAdsGSvc", GmsService.GSERVICES, GmsService.ADREQUEST);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
// TODO
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java
index ed66d88e..16f09301 100644
--- a/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java
+++ b/play-services-core/src/main/java/org/microg/gms/auth/AccountContentProvider.java
@@ -54,7 +54,7 @@ public class AccountContentProvider extends ContentProvider {
if (!PackageUtils.callerHasExtendedAccess(getContext())) {
String[] packagesForUid = getContext().getPackageManager().getPackagesForUid(Binder.getCallingUid());
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]));
if (getContext().checkCallingPermission(Manifest.permission.GET_ACCOUNTS) != PackageManager.PERMISSION_GRANTED)
throw new SecurityException("Access denied, missing GET_ACCOUNTS or EXTENDED_ACCESS permission");
diff --git a/play-services-core/src/main/java/org/microg/gms/auth/SignInService.java b/play-services-core/src/main/java/org/microg/gms/auth/SignInService.java
index adc1f5ef..46fcc722 100644
--- a/play-services-core/src/main/java/org/microg/gms/auth/SignInService.java
+++ b/play-services-core/src/main/java/org/microg/gms/auth/SignInService.java
@@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class SignInService extends BaseService {
public SignInService() {
- super("GmsSignInSvc", Services.SIGN_IN.SERVICE_ID);
+ super("GmsSignInSvc", GmsService.SIGN_IN);
}
@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");
}
diff --git a/play-services-core/src/main/java/org/microg/gms/car/CarService.java b/play-services-core/src/main/java/org/microg/gms/car/CarService.java
index 677ab28a..77492892 100644
--- a/play-services-core/src/main/java/org/microg/gms/car/CarService.java
+++ b/play-services-core/src/main/java/org/microg/gms/car/CarService.java
@@ -20,15 +20,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class CarService extends BaseService {
public CarService() {
- super("GmsCarSvc", Services.CAR.SERVICE_ID);
+ super("GmsCarSvc", GmsService.CAR);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
// TODO
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/clearcut/ClearcutLoggerService.java b/play-services-core/src/main/java/org/microg/gms/clearcut/ClearcutLoggerService.java
index 17f5856f..f636bace 100644
--- a/play-services-core/src/main/java/org/microg/gms/clearcut/ClearcutLoggerService.java
+++ b/play-services-core/src/main/java/org/microg/gms/clearcut/ClearcutLoggerService.java
@@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class ClearcutLoggerService extends BaseService {
private ClearcutLoggerServiceImpl clearcutService = new ClearcutLoggerServiceImpl();
public ClearcutLoggerService() {
- super("GmsClearcutSvc", Services.CLEARCUT_LOGGER.SERVICE_ID);
+ super("GmsClearcutSvc", GmsService.CLEARCUT_LOGGER);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java b/play-services-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java
new file mode 100644
index 00000000..26d24b61
--- /dev/null
+++ b/play-services-core/src/main/java/org/microg/gms/common/MultiListenerProxy.java
@@ -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 implements InvocationHandler {
+ private static final String TAG = "GmsMultiListener";
+
+ public static T get(Class tClass, final Collection listeners) {
+ return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass}, new MultiListenerProxy(listeners));
+ }
+
+ private final Collection listeners;
+
+ private MultiListenerProxy(Collection listeners) {
+ this.listeners = listeners;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ for (T listener : new HashSet(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;
+ }
+}
diff --git a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java
index c17e28c0..de95c9b0 100644
--- a/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java
+++ b/play-services-core/src/main/java/org/microg/gms/common/PackageUtils.java
@@ -28,6 +28,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
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;
public class PackageUtils {
@@ -37,7 +38,8 @@ public class PackageUtils {
"58e1c4133f7441ec3d2c270270a14802da47ba0e" /* Android Wear */,
"46f6c8987311e131f4f558d8e0ae145bebab6da3" /* Google Classroom */,
"24bb24c05e47e0aefa68a58a766179d9b613a600" /* Google Fit/Glass */,
- "aa87ce1260c008d801197bb4ecea4ab8929da246" /* Google Inbox */};
+ "aa87ce1260c008d801197bb4ecea4ab8929da246" /* Google Inbox */,
+ "35b438fe1bc69d975dc8702dc16ab69ebf65f26f" /* Waze */};
public static boolean isGoogleSignedPackages(Context context, String packageName) {
return Arrays.asList(KNOWN_GOOGLE_SIGNATURES).contains(firstSignatureDigest(context, packageName));
@@ -50,8 +52,13 @@ public class PackageUtils {
public static boolean callerHasExtendedAccess(Context context) {
String[] packagesForUid = context.getPackageManager().getPackagesForUid(Binder.getCallingUid());
- return (packagesForUid != null && packagesForUid.length != 0 && isGoogleSignedPackages(context, packagesForUid[0])) ||
- context.checkCallingPermission(Manifest.permission.EXTENDED_ACCESS) == PackageManager.PERMISSION_GRANTED;
+ if (packagesForUid != null && packagesForUid.length != 0) {
+ 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) {
diff --git a/play-services-core/src/main/java/org/microg/gms/drive/api/DriveApiService.java b/play-services-core/src/main/java/org/microg/gms/drive/api/DriveApiService.java
index 28f3e15e..b2b2ae2f 100644
--- a/play-services-core/src/main/java/org/microg/gms/drive/api/DriveApiService.java
+++ b/play-services-core/src/main/java/org/microg/gms/drive/api/DriveApiService.java
@@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class DriveApiService extends BaseService {
private DriveServiceImpl impl = new DriveServiceImpl();
public DriveApiService() {
- super("GmsDriveApiSvc", Services.DRIVE.SERVICE_ID);
+ super("GmsDriveApiSvc", GmsService.DRIVE);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/droidguard/DroidGuardService.java b/play-services-core/src/main/java/org/microg/gms/droidguard/DroidGuardService.java
index ea1efc4a..585c3549 100644
--- a/play-services-core/src/main/java/org/microg/gms/droidguard/DroidGuardService.java
+++ b/play-services-core/src/main/java/org/microg/gms/droidguard/DroidGuardService.java
@@ -20,16 +20,16 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class DroidGuardService extends BaseService {
public DroidGuardService() {
- super("GmsDroidGuardSvc", Services.DROIDGUARD.SERVICE_ID);
+ super("GmsDroidGuardSvc", GmsService.DROIDGUARD);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
// TODO
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/feedback/FeedbackService.java b/play-services-core/src/main/java/org/microg/gms/feedback/FeedbackService.java
index 18b947db..574dcc7a 100644
--- a/play-services-core/src/main/java/org/microg/gms/feedback/FeedbackService.java
+++ b/play-services-core/src/main/java/org/microg/gms/feedback/FeedbackService.java
@@ -20,15 +20,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class FeedbackService extends BaseService {
public FeedbackService() {
- super("GmsFeedbackSvc", Services.FEEDBACK.SERVICE_ID);
+ super("GmsFeedbackSvc", GmsService.FEEDBACK);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) {
// TODO
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/games/GamesStubService.java b/play-services-core/src/main/java/org/microg/gms/games/GamesStubService.java
index 4e07f543..3cb6176f 100644
--- a/play-services-core/src/main/java/org/microg/gms/games/GamesStubService.java
+++ b/play-services-core/src/main/java/org/microg/gms/games/GamesStubService.java
@@ -26,7 +26,7 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
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 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 GamesStubService() {
- super("GmsGamesSvc", GAMES.SERVICE_ID);
+ super("GmsGamesSvc", GmsService.GAMES);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
String packageName = null;
if (request.extras != null) {
packageName = request.extras.getString(PARAM_GAME_PACKAGE_NAME);
diff --git a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
index e154d4c0..3267a8a2 100644
--- a/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
+++ b/play-services-core/src/main/java/org/microg/gms/gcm/McsService.java
@@ -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 int WAKELOCK_TIMEOUT = 5000;
- public static int heartbeatMs = 60000;
+ public static int heartbeatMs = 120000;
private static long lastHeartbeatAckElapsedRealtime = -1;
private static Socket sslSocket;
diff --git a/play-services-core/src/main/java/org/microg/gms/icing/IndexService.java b/play-services-core/src/main/java/org/microg/gms/icing/IndexService.java
index 47641ee9..bf3a15e7 100644
--- a/play-services-core/src/main/java/org/microg/gms/icing/IndexService.java
+++ b/play-services-core/src/main/java/org/microg/gms/icing/IndexService.java
@@ -17,13 +17,14 @@
package org.microg.gms.icing;
import android.os.RemoteException;
+import android.util.Log;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class IndexService extends BaseService {
private AppDataSearchImpl appDataSearch = new AppDataSearchImpl();
@@ -33,30 +34,31 @@ public class IndexService extends BaseService {
public IndexService() {
super("GmsIcingIndexSvc",
- Services.INDEX.SERVICE_ID, Services.SEARCH_ADMINISTRATION.SERVICE_ID,
- Services.SEARCH_CORPORA.SERVICE_ID, Services.SEARCH_GLOBAL.SERVICE_ID,
- Services.SEARCH_IME.SERVICE_ID, Services.SEARCH_QUERIES.SERVICE_ID);
+ GmsService.INDEX, GmsService.SEARCH_ADMINISTRATION, GmsService.SEARCH_CORPORA,
+ GmsService.SEARCH_GLOBAL, GmsService.SEARCH_IME, GmsService.SEARCH_QUERIES);
}
@Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
- switch (request.serviceId) {
- case Services.INDEX.SERVICE_ID:
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
+ switch (service) {
+ case INDEX:
callback.onPostInitComplete(0, appDataSearch.asBinder(), null);
break;
- case Services.SEARCH_ADMINISTRATION.SERVICE_ID:
+ case SEARCH_ADMINISTRATION:
+ Log.w(TAG, "Service not yet implemented: " + service);
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
break;
- case Services.SEARCH_QUERIES.SERVICE_ID:
+ case SEARCH_QUERIES:
callback.onPostInitComplete(0, searchQueries.asBinder(), null);
break;
- case Services.SEARCH_GLOBAL.SERVICE_ID:
+ case SEARCH_GLOBAL:
callback.onPostInitComplete(0, globalSearchAdmin.asBinder(), null);
break;
- case Services.SEARCH_CORPORA.SERVICE_ID:
+ case SEARCH_CORPORA:
callback.onPostInitComplete(0, searchCorpora.asBinder(), null);
break;
- case Services.SEARCH_IME.SERVICE_ID:
+ case SEARCH_IME:
+ Log.w(TAG, "Service not yet implemented: " + service);
callback.onPostInitComplete(CommonStatusCodes.ERROR, null, null);
break;
}
diff --git a/play-services-core/src/main/java/org/microg/gms/icing/LightweightIndexService.java b/play-services-core/src/main/java/org/microg/gms/icing/LightweightIndexService.java
index 37fdb3da..159f0a6c 100644
--- a/play-services-core/src/main/java/org/microg/gms/icing/LightweightIndexService.java
+++ b/play-services-core/src/main/java/org/microg/gms/icing/LightweightIndexService.java
@@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class LightweightIndexService extends BaseService {
private LightweightAppDataSearchImpl appDataSearch = new LightweightAppDataSearchImpl();
public LightweightIndexService() {
- super("GmsIcingLightIndexSvc", Services.LIGHTWEIGHT_INDEX.SERVICE_ID);
+ super("GmsIcingLightIndexSvc", GmsService.LIGHTWEIGHT_INDEX);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerService.java b/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerService.java
index 021953a3..344e8456 100644
--- a/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerService.java
+++ b/play-services-core/src/main/java/org/microg/gms/location/GoogleLocationManagerService.java
@@ -22,18 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class GoogleLocationManagerService extends BaseService {
private GoogleLocationManagerServiceImpl impl = new GoogleLocationManagerServiceImpl(this);
public GoogleLocationManagerService() {
- super("GmsLocManagerSvc", Services.LOCATION_MANAGER.SERVICE_ID,
- Services.GEODATA.SERVICE_ID, Services.PLACE_DETECTION.SERVICE_ID);
+ super("GmsLocManagerSvc", GmsService.LOCATION_MANAGER, GmsService.GEODATA, GmsService.PLACE_DETECTION);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/location/ReportingAndroidService.java b/play-services-core/src/main/java/org/microg/gms/location/ReportingAndroidService.java
index e9f6332c..b48b46ee 100644
--- a/play-services-core/src/main/java/org/microg/gms/location/ReportingAndroidService.java
+++ b/play-services-core/src/main/java/org/microg/gms/location/ReportingAndroidService.java
@@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class ReportingAndroidService extends BaseService {
private ReportingServiceImpl reportingService = new ReportingServiceImpl();
public ReportingAndroidService() {
- super("GmsLocReportingSvc", Services.LOCATION_REPORTING.SERVICE_ID);
+ super("GmsLocReportingSvc", GmsService.LOCATION_REPORTING);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/people/PeopleService.java b/play-services-core/src/main/java/org/microg/gms/people/PeopleService.java
index 396588b5..fdb16af0 100644
--- a/play-services-core/src/main/java/org/microg/gms/people/PeopleService.java
+++ b/play-services-core/src/main/java/org/microg/gms/people/PeopleService.java
@@ -22,17 +22,17 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class PeopleService extends BaseService {
private PeopleServiceImpl impl = new PeopleServiceImpl(this);
public PeopleService() {
- super("GmsPeopleSvc", Services.PEOPLE.SERVICE_ID);
+ super("GmsPeopleSvc", GmsService.PEOPLE);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/places/GeoDataService.java b/play-services-core/src/main/java/org/microg/gms/places/GeoDataService.java
index ce855f1e..ca45ca0b 100644
--- a/play-services-core/src/main/java/org/microg/gms/places/GeoDataService.java
+++ b/play-services-core/src/main/java/org/microg/gms/places/GeoDataService.java
@@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class GeoDataService extends BaseService {
public GeoDataService() {
- super("GmsPlcGeoSvc", Services.GEODATA.SERVICE_ID);
+ super("GmsPlcGeoSvc", GmsService.GEODATA);
}
@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");
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/places/PlaceDetectionService.java b/play-services-core/src/main/java/org/microg/gms/places/PlaceDetectionService.java
index e14dcc7d..985641ca 100644
--- a/play-services-core/src/main/java/org/microg/gms/places/PlaceDetectionService.java
+++ b/play-services-core/src/main/java/org/microg/gms/places/PlaceDetectionService.java
@@ -23,15 +23,15 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class PlaceDetectionService extends BaseService {
public PlaceDetectionService() {
- super("GmsPlcDtctSvc", Services.PLACE_DETECTION.SERVICE_ID);
+ super("GmsPlcDtctSvc", GmsService.PLACE_DETECTION);
}
@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");
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/playlog/PlayLogService.java b/play-services-core/src/main/java/org/microg/gms/playlog/PlayLogService.java
index d220001b..9b4ac260 100644
--- a/play-services-core/src/main/java/org/microg/gms/playlog/PlayLogService.java
+++ b/play-services-core/src/main/java/org/microg/gms/playlog/PlayLogService.java
@@ -22,18 +22,18 @@ import com.google.android.gms.common.internal.GetServiceRequest;
import com.google.android.gms.common.internal.IGmsCallbacks;
import org.microg.gms.BaseService;
-import org.microg.gms.common.Services;
+import org.microg.gms.common.GmsService;
public class PlayLogService extends BaseService {
private PlayLogServiceImpl playLogService = new PlayLogServiceImpl();
public PlayLogService() {
- super("GmsPlayLogSvc", Services.PLAY_LOG.SERVICE_ID);
+ super("GmsPlayLogSvc", GmsService.PLAY_LOG);
}
@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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java
index 00002616..135b1170 100644
--- a/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java
+++ b/play-services-core/src/main/java/org/microg/gms/ui/SettingsActivity.java
@@ -113,14 +113,14 @@ public class SettingsActivity extends AppCompatActivity {
@Override
protected void collectLibraries(List libraries) {
- libraries.add(new Library("org.microg.gms.api", "microG GmsApi", "Apache License 2.0, Copyright © microG Team"));
- libraries.add(new Library("org.microg.safeparcel", "microG SafeParcel", "Apache License 2.0, Copyright © 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.api", "microG UnifiedNlp Api", "Apache License 2.0, Copyright © microG Team"));
- libraries.add(new Library("org.microg.wearable", "microG Wearable", "Apache License 2.0, Copyright © microG Team"));
- libraries.add(new Library("de.hdodenhof.circleimageview", "CircleImageView", "Apache License 2.0, Copyright © Henning Dodenhof"));
- libraries.add(new Library("org.oscim.android", ">map", "GNU LGPLv3, Copyright © Hannes Janetzek"));
- libraries.add(new Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0, Copyright © Square Inc."));
+ 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 by 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, by 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 by Henning Dodenhof"));
+ libraries.add(new Library("org.oscim.android", ">map", "GNU LGPLv3 by Hannes Janetzek"));
+ libraries.add(new Library("com.squareup.wire", "Wire Protocol Buffers", "Apache License 2.0 by Square Inc."));
}
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/DataItemRecord.java b/play-services-core/src/main/java/org/microg/gms/wearable/DataItemRecord.java
index e27f5cc1..479a9b5e 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/DataItemRecord.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/DataItemRecord.java
@@ -136,4 +136,20 @@ public class DataItemRecord {
record.signatureDigest = setDataItem.signatureDigest;
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();
+ }
}
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/MessageHandler.java b/play-services-core/src/main/java/org/microg/gms/wearable/MessageHandler.java
index ceabe7fe..59a7b275 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/MessageHandler.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/MessageHandler.java
@@ -22,7 +22,6 @@ import android.util.Log;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.ConnectionConfiguration;
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.common.Build;
@@ -44,14 +43,15 @@ import java.util.Arrays;
public class MessageHandler extends ServerMessageListener {
private static final String TAG = "GmsWearMsgHandler";
- private final WearableServiceImpl service;
- private final ConnectionConfiguration config;
+ private final WearableImpl wearable;
+ private final String thisNodeId;
+ private String peerNodeId;
- public MessageHandler(WearableServiceImpl service, ConnectionConfiguration config) {
- this(service, config, new Build().model, config.nodeId, LastCheckinInfo.read(service.getContext()).androidId);
+ public MessageHandler(WearableImpl wearable, ConnectionConfiguration config) {
+ 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()
.name(name)
.id(config.nodeId)
@@ -60,24 +60,23 @@ public class MessageHandler extends ServerMessageListener {
.unknown4(3)
.unknown5(1)
.build());
- this.service = service;
- this.config = config;
+ this.wearable = wearable;
+ this.thisNodeId = config.nodeId;
}
@Override
public void onConnect(Connect connect) {
super.onConnect(connect);
- config.peerNodeId = connect.id;
- config.connected = true;
- service.onPeerConnected(new NodeParcelable(connect.id, connect.name));
+ peerNodeId = connect.id;
+ wearable.onConnectReceived(getConnection(), thisNodeId, connect);
try {
getConnection().writeMessage(new RootMessage.Builder().syncStart(new SyncStart.Builder()
.receivedSeqId(-1L)
.version(2)
.syncTable(Arrays.asList(
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(config.peerNodeId).value(service.getCurrentSeqId(config.peerNodeId)).build() // TODO
+ new SyncTableEntry.Builder().key(thisNodeId).value(wearable.getCurrentSeqId(thisNodeId)).build(), // TODO
+ new SyncTableEntry.Builder().key(peerNodeId).value(wearable.getCurrentSeqId(peerNodeId)).build() // TODO
)).build()).build());
} catch (IOException e) {
Log.w(TAG, e);
@@ -93,7 +92,7 @@ public class MessageHandler extends ServerMessageListener {
} else {
asset = Asset.createFromRef(setAsset.digest);
}
- service.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
+ wearable.addAssetToDatabase(asset, setAsset.appkeys.appKeys);
}
@Override
@@ -115,34 +114,34 @@ public class MessageHandler extends ServerMessageListener {
boolean hasLocalNode = false;
if (syncStart.syncTable != null) {
for (SyncTableEntry entry : syncStart.syncTable) {
- service.syncToPeer(getConnection(), entry.key, entry.value);
- if (service.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
+ wearable.syncToPeer(getConnection(), entry.key, entry.value);
+ if (wearable.getLocalNodeId().equals(entry.key)) hasLocalNode = true;
}
} else {
Log.d(TAG, "No sync table given.");
}
- if (!hasLocalNode) service.syncToPeer(getConnection(), service.getLocalNodeId(), 0);
+ if (!hasLocalNode) wearable.syncToPeer(getConnection(), wearable.getLocalNodeId(), 0);
}
@Override
public void onSetDataItem(SetDataItem setDataItem) {
Log.d(TAG, "onSetDataItem: " + setDataItem);
- service.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
+ wearable.putDataItem(DataItemRecord.fromSetDataItem(setDataItem));
}
@Override
- public void onRcpRequest(Request rcpRequest) {
- Log.d(TAG, "onRcpRequest: " + rcpRequest);
- if (TextUtils.isEmpty(rcpRequest.targetNodeId)) {
+ public void onRpcRequest(Request rpcRequest) {
+ Log.d(TAG, "onRpcRequest: " + rpcRequest);
+ if (TextUtils.isEmpty(rpcRequest.targetNodeId)) {
// TODO: That's probably not how it should go!
MessageEventParcelable messageEvent = new MessageEventParcelable();
- messageEvent.data = rcpRequest.rawData != null ? rcpRequest.rawData.toByteArray() : null;
- messageEvent.path = rcpRequest.path;
- messageEvent.requestId = rcpRequest.requestId + 31 * (rcpRequest.generation + 527);
- messageEvent.sourceNodeId = TextUtils.isEmpty(rcpRequest.sourceNodeId) ? config.peerNodeId : rcpRequest.sourceNodeId;
+ messageEvent.data = rpcRequest.rawData != null ? rpcRequest.rawData.toByteArray() : null;
+ messageEvent.path = rpcRequest.path;
+ messageEvent.requestId = rpcRequest.requestId + 31 * (rpcRequest.generation + 527);
+ messageEvent.sourceNodeId = TextUtils.isEmpty(rpcRequest.sourceNodeId) ? peerNodeId : rpcRequest.sourceNodeId;
- service.onMessageReceived(messageEvent);
- } else if (rcpRequest.targetNodeId.equals(config.peerNodeId)) {
+ wearable.sendMessageReceived(messageEvent);
+ } else if (rpcRequest.targetNodeId.equals(peerNodeId)) {
// Drop it, loop detection (yes we really need this in this protocol o.O)
} else {
// 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
public void onFilePiece(FilePiece 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
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/NetworkConnectionThread.java b/play-services-core/src/main/java/org/microg/gms/wearable/NetworkConnectionThread.java
index ab682f17..0e00e4e6 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/NetworkConnectionThread.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/NetworkConnectionThread.java
@@ -21,6 +21,7 @@ import android.util.Log;
import com.google.android.gms.wearable.ConnectionConfiguration;
import org.microg.wearable.SocketWearableConnection;
+import org.microg.wearable.WearableConnection;
import java.io.IOException;
import java.net.ServerSocket;
@@ -33,10 +34,11 @@ class NetworkConnectionThread extends Thread {
private ConnectionConfiguration config;
private ServerSocket socket;
private MessageHandler handler;
+ private WearableConnection wearableConnection;
- public NetworkConnectionThread(WearableServiceImpl service, ConnectionConfiguration config) {
+ public NetworkConnectionThread(WearableImpl wearable, ConnectionConfiguration config) {
this.config = config;
- this.handler = new MessageHandler(service, config);
+ this.handler = new MessageHandler(wearable, config);
}
public void close() {
@@ -47,13 +49,17 @@ class NetworkConnectionThread extends Thread {
}
}
+ public WearableConnection getWearableConnection() {
+ return wearableConnection;
+ }
+
@Override
public void run() {
try {
socket = new ServerSocket(WEAR_TCP_PORT);
Log.d(TAG, "Listening for connections on TCP :" + WEAR_TCP_PORT);
Socket accepted = socket.accept();
- new SocketWearableConnection(accepted, handler).run();
+ (wearableConnection = new SocketWearableConnection(accepted, handler)).run();
Log.d(TAG, "Connection terminated, me too");
} catch (IOException e) {
Log.w(TAG, e);
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java b/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java
index 9c4ddf64..d59f5d98 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/NodeDatabaseHelper.java
@@ -22,7 +22,6 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
-import android.util.Log;
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});
if (cursor != null) {
try {
@@ -143,22 +142,28 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
}
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.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
contentValues.put("host", record.dataItem.host);
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) {
for (Map.Entry asset : record.dataItem.getAssets().entrySet()) {
ContentValues assetValues = new ContentValues();
assetValues.put("assets_digest", asset.getValue().getDigest());
assetValues.put("dataitems_id", key);
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);
if (status.moveToNext()) {
@@ -230,14 +235,14 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
cv.put("digest", asset.getDigest());
cv.put("dataPresent", dataPresent ? 1 : 0);
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) {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java b/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java
new file mode 100644
index 00000000..498540da
--- /dev/null
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/WearableImpl.java
@@ -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 listeners = new HashSet();
+ private final Set connectedNodes = new HashSet();
+ private final Set activeConnections = new HashSet();
+ 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(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(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 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 getConnectedNodesParcelableList() {
+ List nodes = new ArrayList();
+ 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 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 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;
+ }
+}
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/WearableService.java b/play-services-core/src/main/java/org/microg/gms/wearable/WearableService.java
index c9ebc5d3..ea1835c1 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/WearableService.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/WearableService.java
@@ -16,6 +16,7 @@
package org.microg.gms.wearable;
+import android.content.Context;
import android.os.Binder;
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 org.microg.gms.BaseService;
+import org.microg.gms.common.GmsService;
import org.microg.gms.common.PackageUtils;
-import org.microg.gms.common.Services;
public class WearableService extends BaseService {
- private ConfigurationDatabaseHelper configurationDatabaseHelper;
- private NodeDatabaseHelper nodeDatabaseHelper;
+ private static WearableImpl wearable;
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
- public void onCreate() {
- super.onCreate();
- configurationDatabaseHelper = new ConfigurationDatabaseHelper(this);
- nodeDatabaseHelper = new NodeDatabaseHelper(this);
- }
-
- @Override
- public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request) throws RemoteException {
+ public void handleServiceRequest(IGmsCallbacks callback, GetServiceRequest request, GmsService service) throws RemoteException {
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);
}
}
diff --git a/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java b/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java
index f9e61fbd..a92e8d84 100644
--- a/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java
+++ b/play-services-core/src/main/java/org/microg/gms/wearable/WearableServiceImpl.java
@@ -17,26 +17,14 @@
package org.microg.gms.wearable;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Base64;
import android.util.Log;
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.Node;
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.GetConfigResponse;
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.GetLocalNodeResponse;
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.MessageEventParcelable;
import com.google.android.gms.wearable.internal.NodeParcelable;
import com.google.android.gms.wearable.internal.PutDataRequest;
import com.google.android.gms.wearable.internal.PutDataResponse;
import com.google.android.gms.wearable.internal.RemoveListenerRequest;
-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.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 {
+public class WearableServiceImpl extends IWearableService.Stub {
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 String packageName;
- private final NodeDatabaseHelper nodeDatabase;
- private final ConfigurationDatabaseHelper configDatabase;
- private Set listeners = new HashSet();
- private Set connectedNodes = new HashSet();
- private ConnectionConfiguration[] configurations;
- private boolean configurationsUpdated = false;
- private NetworkConnectionThread nct;
+ private final WearableImpl wearable;
- private long seqIdBlock;
- private long seqIdInBlock = -1;
-
- public WearableServiceImpl(Context context, NodeDatabaseHelper nodeDatabase, ConfigurationDatabaseHelper configDatabase, String packageName) {
+ public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) {
this.context = context;
- this.nodeDatabase = nodeDatabase;
- this.configDatabase = configDatabase;
+ this.wearable = wearable;
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
public void putData(IWearableCallbacks callbacks, PutDataRequest request) throws RemoteException {
Log.d(TAG, "putData: " + request.toString(true));
- String host = request.getUri().getHost();
- if (TextUtils.isEmpty(host)) host = getLocalNodeId();
- DataItemInternal dataItem = new DataItemInternal(host, request.getUri().getPath());
- for (Map.Entry 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);
- }
+ DataItemRecord record = wearable.putData(request, packageName);
+ callbacks.onPutDataResponse(new PutDataResponse(0, record.toParcelable()));
}
@Override
public void getDataItem(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
Log.d(TAG, "getDataItem: " + uri);
- Cursor cursor = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
- if (cursor != null) {
- if (cursor.moveToNext()) {
- DataItemParcelable dataItem = new DataItemParcelable(new Uri.Builder().scheme("wear").authority(cursor.getString(0)).path(cursor.getString(1)).build());
- dataItem.data = cursor.getBlob(2);
- Log.d(TAG, "getDataItem.asset " + cursor.getString(5));
- // TODO: assets
- callbacks.onGetDataItemResponse(new GetDataItemResponse(0, dataItem));
- }
- cursor.close();
+
+ DataItemRecord record = wearable.getDataItemByUri(uri, packageName);
+ if (record != null) {
+ callbacks.onGetDataItemResponse(new GetDataItemResponse(0, record.toParcelable()));
+ } else {
+ // TODO: negative
}
- // TODO: negative
}
@Override
public void getDataItems(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getDataItems: " + callbacks);
- 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();
- callbacks.onDataHolder(DataHolder.fromCursor(dataHolderItems, 0, null));
+ callbacks.onDataHolder(wearable.getDataItems(packageName));
}
@Override
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri, int i) throws RemoteException {
Log.d(TAG, "getDataItemsByUri: " + uri);
- Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
- callbacks.onDataHolder(DataHolder.fromCursor(dataHolderItems, 0, null));
+ callbacks.onDataHolder(wearable.getDataItemsByUri(uri, packageName));
}
@Override
public void deleteDataItems(IWearableCallbacks callbacks, Uri uri) throws RemoteException {
Log.d(TAG, "deleteDataItems: " + uri);
- int count = nodeDatabase.deleteDataItems(packageName, PackageUtils.firstSignatureDigest(context, packageName), uri.getHost(), uri.getPath());
- callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, count));
+ callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName)));
}
@Override
@@ -267,7 +97,7 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
@Override
public void getLocalNode(IWearableCallbacks callbacks) throws RemoteException {
try {
- callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(getLocalNodeId(), getLocalNodeId())));
+ callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(0, new NodeParcelable(wearable.getLocalNodeId(), wearable.getLocalNodeId())));
} catch (Exception e) {
callbacks.onGetLocalNodeResponse(new GetLocalNodeResponse(8, null));
}
@@ -276,51 +106,41 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
@Override
public void getConnectedNodes(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getConnectedNodes");
- callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, getConnectedNodesParcelableList()));
- }
-
- private List getConnectedNodesParcelableList() {
- List nodes = new ArrayList();
- for (Node connectedNode : connectedNodes) {
- nodes.add(new NodeParcelable(connectedNode));
- }
- return nodes;
+ callbacks.onGetConnectedNodesResponse(new GetConnectedNodesResponse(0, wearable.getConnectedNodesParcelableList()));
}
@Override
public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
Log.d(TAG, "addListener[nyp]: " + request);
- listeners.add(request.listener);
+ if (request.listener != null) {
+ wearable.addListener(request.listener);
+ }
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void removeListener(IWearableCallbacks callbacks, RemoveListenerRequest request) throws RemoteException {
Log.d(TAG, "removeListener[nyp]: " + request);
- listeners.remove(request.listener);
+ wearable.removeListener(request.listener);
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void putConfig(IWearableCallbacks callbacks, ConnectionConfiguration config) throws RemoteException {
- if (config.nodeId == null) config.nodeId = getLocalNodeId();
- Log.d(TAG, "putConfig[nyp]: " + config);
- configDatabase.putConfiguration(config);
- configurationsUpdated = true;
+ wearable.createConnection(config);
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void deleteConfig(IWearableCallbacks callbacks, String name) throws RemoteException {
- configDatabase.deleteConfiguration(name);
- configurationsUpdated = true;
+ wearable.deleteConnection(name);
callbacks.onStatus(Status.SUCCESS);
}
@Override
public void getConfig(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getConfig");
- ConnectionConfiguration[] configurations = getConfigurations();
+ ConnectionConfiguration[] configurations = wearable.getConfigurations();
if (configurations == null || configurations.length == 0) {
callbacks.onGetConfigResponse(new GetConfigResponse(1, new ConnectionConfiguration(null, null, 0, 0, false)));
} else {
@@ -332,59 +152,25 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
public void getConfigs(IWearableCallbacks callbacks) throws RemoteException {
Log.d(TAG, "getConfigs");
try {
- callbacks.onGetConfigsResponse(new GetConfigsResponse(0, getConfigurations()));
+ callbacks.onGetConfigsResponse(new GetConfigsResponse(0, wearable.getConfigurations()));
} catch (Exception e) {
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
public void enableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
Log.d(TAG, "enableConnection: " + name);
- configDatabase.setEnabledState(name, true);
- configurationsUpdated = true;
+ wearable.enableConnection(name);
callbacks.onStatus(Status.SUCCESS);
- if (name.equals("server")) {
- // TODO: hackady hack
- (nct = new NetworkConnectionThread(this, configDatabase.getConfiguration(name))).start();
- }
}
@Override
public void disableConnection(IWearableCallbacks callbacks, String name) throws RemoteException {
Log.d(TAG, "disableConnection: " + name);
- configDatabase.setEnabledState(name, false);
- configurationsUpdated = true;
+ wearable.disableConnection(name);
callbacks.onStatus(Status.SUCCESS);
- if (name.equals("server")) {
- // TODO: hacady hack
- if (nct != null) {
- nct.close();
- nct.interrupt();
- nct = null;
- }
- }
}
@Override
@@ -393,195 +179,4 @@ public class WearableServiceImpl extends IWearableService.Stub implements IWeara
Log.d(TAG, "onTransact [unknown]: " + code + ", " + data + ", " + flags);
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(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(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 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 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);
- }
- }
- }
}
diff --git a/proguard.flags b/proguard.flags
index add05b04..86d9be1b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -17,25 +17,25 @@
-dontnote
# Keep dynamically loaded GMS classes
--keep public class com.google.android.gms.maps.internal.CreatorImpl
--keep public class com.google.android.gms.common.security.ProviderInstallerImpl
--keep public class com.google.android.gms.plus.plusone.PlusOneButtonCreatorImpl
-
--keepclassmembers class com.google.android.gms.common.security.ProviderInstallerImpl {
- public *;
-}
+-keep public class com.google.android.gms.maps.internal.CreatorImpl { public *; }
+-keep public class com.google.android.gms.common.security.ProviderInstallerImpl { public *; }
+-keep public class com.google.android.gms.plus.plusone.PlusOneButtonCreatorImpl { public *; }
# Keep AutoSafeParcelables
--keep public class * extends org.microg.safeparcel.AutoSafeParcelable
--keepclassmembers public class * extends org.microg.safeparcel.AutoSafeParcelable {
+-keep public class * extends org.microg.safeparcel.AutoSafeParcelable {
@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 class **.BuildConfig
--keepclassmembers class **.BuildConfig { *; }
+-keep class **.BuildConfig { *; }
# Keep protobuf class builders
-keep public class * extends com.squareup.wire.Message
--keep public class * extends com.squareup.wire.Message$Builder
--keepclassmembers class * extends com.squareup.wire.Message$Builder { public (...); }
+-keep public class * extends com.squareup.wire.Message$Builder { public (...); }