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 (...); }