mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2024-12-22 10:37:45 +01:00
Wearable: correctly implement listeners including remote binding
This commit is contained in:
parent
8cb49f7c98
commit
27c1c540d6
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RemoteListenerProxy<T extends IInterface> implements ServiceConnection, InvocationHandler {
|
||||
private static final String TAG = "GmsRemoteListener";
|
||||
private final Context context;
|
||||
private final Intent searchIntent;
|
||||
private final String bindAction;
|
||||
private IBinder remote;
|
||||
private boolean connecting;
|
||||
private List<Runnable> waiting = new ArrayList<Runnable>();
|
||||
private Class<T> tClass;
|
||||
|
||||
public static <T extends IInterface> T get(Context context, Intent intent, Class<T> tClass, String bindAction) {
|
||||
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[]{tClass},
|
||||
new RemoteListenerProxy<T>(context, intent, tClass, bindAction));
|
||||
}
|
||||
|
||||
private RemoteListenerProxy(Context context, Intent intent, Class<T> tClass, String bindAction) {
|
||||
this.context = context;
|
||||
this.searchIntent = intent;
|
||||
this.tClass = tClass;
|
||||
this.bindAction = bindAction;
|
||||
}
|
||||
|
||||
private boolean connect() {
|
||||
synchronized (this) {
|
||||
if (!connecting) {
|
||||
try {
|
||||
ResolveInfo resolveInfo = context.getPackageManager().resolveService(searchIntent, 0);
|
||||
if (resolveInfo != null) {
|
||||
Intent intent = new Intent(bindAction);
|
||||
intent.setPackage(resolveInfo.serviceInfo.packageName);
|
||||
intent.setClassName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
|
||||
connecting = context.bindService(intent, this, Context.BIND_AUTO_CREATE);
|
||||
if (!connecting) Log.d(TAG, "Could not connect to: " + intent);
|
||||
return connecting;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void runOncePossible(Runnable runnable) {
|
||||
synchronized (this) {
|
||||
if (remote == null) {
|
||||
waiting.add(runnable);
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
synchronized (this) {
|
||||
remote = service;
|
||||
if (!waiting.isEmpty()) {
|
||||
for (Runnable runnable : waiting) {
|
||||
runnable.run();
|
||||
}
|
||||
waiting.clear();
|
||||
context.unbindService(RemoteListenerProxy.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
synchronized (this) {
|
||||
remote = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
|
||||
if (method.getDeclaringClass().equals(tClass)) {
|
||||
runOncePossible(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Object asInterface = Class.forName(tClass.getName() + "$Stub").getMethod("asInterface", IBinder.class).invoke(null, remote);
|
||||
method.invoke(asInterface, args);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
connect();
|
||||
return null;
|
||||
} else if (method.getDeclaringClass().equals(Object.class)) {
|
||||
return method.invoke(this, args);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.android.gms.common.data.DataHolder;
|
||||
import com.google.android.gms.wearable.Asset;
|
||||
import com.google.android.gms.wearable.internal.DataItemAssetParcelable;
|
||||
import com.google.android.gms.wearable.internal.DataItemParcelable;
|
||||
@ -28,12 +29,15 @@ import org.microg.wearable.proto.AssetEntry;
|
||||
import org.microg.wearable.proto.SetDataItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import okio.ByteString;
|
||||
|
||||
public class DataItemRecord {
|
||||
private static String[] EVENT_DATA_HOLDER_FIELDS = new String[] { "event_type", "path", "data", "tags", "asset_key", "asset_id" };
|
||||
|
||||
public DataItemInternal dataItem;
|
||||
public String source;
|
||||
public long seqId;
|
||||
@ -44,7 +48,7 @@ public class DataItemRecord {
|
||||
public String packageName;
|
||||
public String signatureDigest;
|
||||
|
||||
public ContentValues getContentValues() {
|
||||
public ContentValues toContentValues() {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put("sourceNode", source);
|
||||
contentValues.put("seqId", seqId);
|
||||
@ -62,6 +66,33 @@ public class DataItemRecord {
|
||||
return contentValues;
|
||||
}
|
||||
|
||||
public DataHolder toEventDataHolder() {
|
||||
DataHolder.Builder builder = DataHolder.builder(EVENT_DATA_HOLDER_FIELDS);
|
||||
HashMap<String, Object> data = new HashMap<String, Object>();
|
||||
data.put("path", dataItem.uri.toString());
|
||||
if (deleted) {
|
||||
data.put("event_type", 2);
|
||||
builder.withRow(data);
|
||||
} else {
|
||||
data.put("event_type", 1);
|
||||
data.put("data", dataItem.data);
|
||||
data.put("tags", "");
|
||||
boolean added = false;
|
||||
for (Map.Entry<String, Asset> entry : dataItem.getAssets().entrySet()) {
|
||||
added = true;
|
||||
data.put("asset_id", entry.getValue().getDigest());
|
||||
data.put("asset_key", entry.getKey());
|
||||
builder.withRow(data);
|
||||
data = new HashMap<String, Object>();
|
||||
data.put("path", dataItem.uri.toString());
|
||||
}
|
||||
if (!added) {
|
||||
builder.withRow(data);
|
||||
}
|
||||
}
|
||||
return builder.build(0);
|
||||
}
|
||||
|
||||
public DataItemParcelable toParcelable() {
|
||||
DataItemParcelable parcelable = new DataItemParcelable(dataItem.uri);
|
||||
parcelable.data = dataItem.data;
|
||||
|
@ -145,7 +145,7 @@ public class MessageHandler extends ServerMessageListener {
|
||||
messageEvent.requestId = rpcRequest.requestId + 31 * (rpcRequest.generation + 527);
|
||||
messageEvent.sourceNodeId = TextUtils.isEmpty(rpcRequest.sourceNodeId) ? peerNodeId : rpcRequest.sourceNodeId;
|
||||
|
||||
wearable.sendMessageReceived(messageEvent);
|
||||
wearable.sendMessageReceived(rpcRequest.packageName, messageEvent);
|
||||
} else if (rpcRequest.targetNodeId.equals(peerNodeId)) {
|
||||
// Drop it, loop detection (yes we really need this in this protocol o.O)
|
||||
} else {
|
||||
|
@ -147,13 +147,13 @@ public class NodeDatabaseHelper extends SQLiteOpenHelper {
|
||||
}
|
||||
|
||||
private static void updateRecord(SQLiteDatabase db, String key, DataItemRecord record) {
|
||||
ContentValues cv = record.getContentValues();
|
||||
ContentValues cv = record.toContentValues();
|
||||
db.update("dataitems", cv, "_id=?", new String[]{key});
|
||||
finishRecord(db, key, record);
|
||||
}
|
||||
|
||||
private static String insertRecord(SQLiteDatabase db, DataItemRecord record) {
|
||||
ContentValues contentValues = record.getContentValues();
|
||||
ContentValues contentValues = record.toContentValues();
|
||||
contentValues.put("appkeys_id", getAppKey(db, record.packageName, record.signatureDigest));
|
||||
contentValues.put("host", record.dataItem.host);
|
||||
contentValues.put("path", record.dataItem.path);
|
||||
|
@ -17,10 +17,10 @@
|
||||
package org.microg.gms.wearable;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
@ -36,6 +36,7 @@ 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.RemoteListenerProxy;
|
||||
import org.microg.gms.common.Utils;
|
||||
import org.microg.wearable.SocketConnectionThread;
|
||||
import org.microg.wearable.WearableConnection;
|
||||
@ -55,11 +56,9 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@ -110,11 +109,8 @@ public class WearableImpl {
|
||||
public DataItemRecord putDataItem(DataItemRecord record) {
|
||||
nodeDatabase.putRecord(record);
|
||||
try {
|
||||
if (listeners.containsKey(record.packageName)) {
|
||||
MultiListenerProxy.get(IWearableListener.class, listeners.get(record.packageName)).onDataChanged(getDataItemForRecord(record));
|
||||
} else {
|
||||
|
||||
}
|
||||
getListener(record.packageName, "com.google.android.gms.wearable.DATA_CHANGED", record.dataItem.uri)
|
||||
.onDataChanged(record.toEventDataHolder());
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@ -402,7 +398,7 @@ public class WearableImpl {
|
||||
return record;
|
||||
}
|
||||
|
||||
public DataHolder getDataItems(String packageName) {
|
||||
public DataHolder getDataItemsAsHolder(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());
|
||||
@ -412,7 +408,7 @@ public class WearableImpl {
|
||||
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||
}
|
||||
|
||||
public DataHolder getDataItemsByUri(Uri uri, String packageName) {
|
||||
public DataHolder getDataItemsByUriAsHolder(Uri uri, String packageName) {
|
||||
String firstSignature;
|
||||
try {
|
||||
firstSignature = PackageUtils.firstSignatureDigest(context, packageName);
|
||||
@ -428,7 +424,7 @@ public class WearableImpl {
|
||||
return DataHolder.fromCursor(dataHolderItems, 0, null);
|
||||
}
|
||||
|
||||
public DataHolder getDataItemForRecord(DataItemRecord record) {
|
||||
public DataHolder getDataItemForRecordAsHolder(DataItemRecord record) {
|
||||
Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(record.packageName, record.signatureDigest, record.dataItem.uri.getHost(), record.dataItem.uri.getPath());
|
||||
while (dataHolderItems.moveToNext()) {
|
||||
Log.d(TAG, "getDataItems[]: path=" + Uri.parse(dataHolderItems.getString(1)).getPath());
|
||||
@ -490,10 +486,11 @@ public class WearableImpl {
|
||||
return records.size();
|
||||
}
|
||||
|
||||
public void sendMessageReceived(MessageEventParcelable messageEvent) {
|
||||
public void sendMessageReceived(String packageName, MessageEventParcelable messageEvent) {
|
||||
Log.d(TAG, "onMessageReceived: " + messageEvent);
|
||||
try {
|
||||
getAllListeners().onMessageReceived(messageEvent);
|
||||
getListener(packageName, "com.google.android.gms.wearable.MESSAGE_RECEIVED", Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath()))
|
||||
.onMessageReceived(messageEvent);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, e);
|
||||
}
|
||||
@ -510,4 +507,22 @@ public class WearableImpl {
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
private IWearableListener getListener(String packageName, String action, Uri uri) {
|
||||
synchronized (this) {
|
||||
List<IWearableListener> l = new ArrayList<IWearableListener>(listeners.containsKey(packageName) ? listeners.get(packageName) : Collections.<IWearableListener>emptyList());
|
||||
|
||||
Intent intent = new Intent(action);
|
||||
intent.setPackage(packageName);
|
||||
intent.setData(uri);
|
||||
|
||||
l.add(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER"));
|
||||
|
||||
return MultiListenerProxy.get(IWearableListener.class, l);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(String targetNodeId, String path, byte[] data) {
|
||||
Log.d(TAG, "sendMessage not yet implemented!");
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.api.Status;
|
||||
@ -75,13 +74,19 @@ public class WearableServiceImpl extends IWearableService.Stub {
|
||||
@Override
|
||||
public void getDataItems(IWearableCallbacks callbacks) throws RemoteException {
|
||||
Log.d(TAG, "getDataItems: " + callbacks);
|
||||
callbacks.onDataHolder(wearable.getDataItems(packageName));
|
||||
callbacks.onDataHolder(wearable.getDataItemsAsHolder(packageName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(IWearableCallbacks callbacks, String targetNodeId, String path, byte[] data) throws RemoteException {
|
||||
Log.d(TAG, "sendMessage: " + targetNodeId + " / " + path);
|
||||
wearable.sendMessage(targetNodeId, path, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDataItemsByUri(IWearableCallbacks callbacks, Uri uri, int i) throws RemoteException {
|
||||
Log.d(TAG, "getDataItemsByUri: " + uri);
|
||||
callbacks.onDataHolder(wearable.getDataItemsByUri(uri, packageName));
|
||||
callbacks.onDataHolder(wearable.getDataItemsByUriAsHolder(uri, packageName));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user