Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit 3c5e6840 authored by Marvin W.'s avatar Marvin W. 🐿️
Browse files

Improvements to Wear code for emulator

parent 6ba9bbb0
Loading
Loading
Loading
Loading
+10 −5
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013-2017 microG Project Team
 * Copyright (C) 2013-2019 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.
@@ -66,7 +66,6 @@ public class RemoteListenerProxy<T extends IInterface> implements ServiceConnect
                        if (!connecting) Log.d(TAG, "Could not connect to: " + intent);
                        return connecting;
                    }
                    Log.d(TAG, "Unable to resolve: " + searchIntent);
                    return false;
                } catch (Exception e) {
                    Log.w(TAG, e);
@@ -91,11 +90,17 @@ public class RemoteListenerProxy<T extends IInterface> implements ServiceConnect
        synchronized (this) {
            remote = service;
            if (!waiting.isEmpty()) {
                try {
                    for (Runnable runnable : waiting) {
                        runnable.run();
                    }
                } catch (Exception e) {
                }
                waiting.clear();
                try {
                    context.unbindService(RemoteListenerProxy.this);
                } catch (Exception e) {
                }
                connecting = false;
                remote = null;
            }
+106 −107
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013-2017 microG Project Team
 * Copyright (C) 2013-2019 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.
@@ -16,14 +16,15 @@

package org.microg.gms.wearable;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
@@ -37,7 +38,6 @@ 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.RemoteListenerProxy;
import org.microg.gms.common.Utils;
@@ -52,6 +52,7 @@ import org.microg.wearable.proto.FilePiece;
import org.microg.wearable.proto.Request;
import org.microg.wearable.proto.RootMessage;
import org.microg.wearable.proto.SetAsset;
import org.microg.wearable.proto.SetDataItem;

import java.io.File;
import java.io.FileInputStream;
@@ -70,8 +71,6 @@ import java.util.Set;

import okio.ByteString;

import static android.os.Build.VERSION.SDK_INT;

public class WearableImpl {

    private static final String TAG = "GmsWear";
@@ -81,7 +80,7 @@ public class WearableImpl {
    private final Context context;
    private final NodeDatabaseHelper nodeDatabase;
    private final ConfigurationDatabaseHelper configDatabase;
    private final Map<String, List<IWearableListener>> listeners = new HashMap<String, List<IWearableListener>>();
    private final Map<String, List<ListenerInfo>> listeners = new HashMap<String, List<ListenerInfo>>();
    private final Set<Node> connectedNodes = new HashSet<Node>();
    private final Map<String, WearableConnection> activeConnections = new HashMap<String, WearableConnection>();
    private RpcHelper rpcHelper;
@@ -130,12 +129,10 @@ public class WearableImpl {
                }
            }
        }
        try {
            getListener(record.packageName, "com.google.android.gms.wearable.DATA_CHANGED", record.dataItem.uri)
                    .onDataChanged(record.toEventDataHolder());
        } catch (RemoteException e) {
            Log.w(TAG, e);
        }
        Intent intent = new Intent("com.google.android.gms.wearable.DATA_CHANGED");
        intent.setPackage(record.packageName);
        intent.setData(record.dataItem.uri);
        invokeListeners(intent, listener -> listener.onDataChanged(record.toEventDataHolder()));
        return record;
    }

@@ -247,8 +244,7 @@ public class WearableImpl {
    }


    private void syncRecordToAll(DataItemRecord record) {
        Log.d(TAG, "Syncing record " + record + " over " + activeConnections.size() + " connections.");
    void syncRecordToAll(DataItemRecord record) {
        for (String nodeId : new ArrayList<String>(activeConnections.keySet())) {
            syncRecordToPeer(nodeId, record);
        }
@@ -256,23 +252,27 @@ public class WearableImpl {

    private boolean syncRecordToPeer(String nodeId, DataItemRecord record) {
        for (Asset asset : record.dataItem.getAssets().values()) {
            try {
                syncAssetToPeer(nodeId, record, asset);
            } catch (Exception e) {
                Log.w(TAG, "Could not sync asset " + asset + " for " + nodeId + " and " + record, e);
                closeConnection(nodeId);
                return false;
            }
        }
        Log.d(TAG, "Sync over to " + nodeId + ": " + record);

        try {
            activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(record.toSetDataItem()).build());
        } catch (IOException e) {
            closeConnection(nodeId);
            SetDataItem item = record.toSetDataItem();
            activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().setDataItem(item).build());
        } catch (Exception e) {
            Log.w(TAG, e);
            closeConnection(nodeId);
            return false;
        }
        return true;
    }

    private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) {
        try {
            Log.d(TAG, "Sync over to " + nodeId + ": " + asset);
    private void syncAssetToPeer(String nodeId, DataItemRecord record, Asset asset) throws IOException {
        RootMessage announceMessage = new RootMessage.Builder().setAsset(new SetAsset.Builder()
                .digest(asset.getDigest())
                .appkeys(new AppKeys(Collections.singletonList(new AppKey(record.packageName, record.signatureDigest))))
@@ -286,17 +286,11 @@ public class WearableImpl {
        int c = 0;
        while ((c = fis.read(arr)) > 0) {
            if (lastPiece != null) {
                    Log.d(TAG, "Sync over to " + nodeId + ": Asset piece for fileName " + fileName + ": " + lastPiece);
                activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, false, lastPiece, null)).build());
            }
            lastPiece = ByteString.of(arr, 0, c);
        }
            Log.d(TAG, "Sync over to " + nodeId + ": Last asset piece for fileName " + fileName + ": " + lastPiece);
        activeConnections.get(nodeId).writeMessage(new RootMessage.Builder().filePiece(new FilePiece(fileName, true, lastPiece, asset.getDigest())).build());
        } catch (IOException e) {
            Log.w(TAG, e);
            closeConnection(nodeId);
        }
    }

    public void addAssetToDatabase(Asset asset, List<AppKey> appKeys) {
@@ -394,37 +388,59 @@ public class WearableImpl {
        return nodes;
    }

    public IWearableListener getAllListeners() {
        return MultiListenerProxy.get(IWearableListener.class, new MultiListenerProxy.MultiCollectionListenerPool<IWearableListener>(listeners.values()));
    interface ListenerInvoker {
        void invoke(IWearableListener listener) throws RemoteException;
    }

    public void onPeerConnected(NodeParcelable node) {
        Log.d(TAG, "onPeerConnected: " + node);
    private void invokeListeners(@Nullable Intent intent, ListenerInvoker invoker) {
        for (String packageName : new ArrayList<>(listeners.keySet())) {
            List<ListenerInfo> listeners = this.listeners.get(packageName);
            if (listeners == null) continue;
            for (int i = 0; i < listeners.size(); i++) {
                boolean filterMatched = false;
                if (intent != null) {
                    for (IntentFilter filter : listeners.get(i).filters) {
                        filterMatched |= filter.match(context.getContentResolver(), intent, false, TAG) > 0;
                    }
                }
                if (filterMatched || listeners.get(i).filters.length == 0) {
                    try {
            getAllListeners().onPeerConnected(node);
                        invoker.invoke(listeners.get(i).listener);
                    } catch (RemoteException e) {
            Log.w(TAG, e);
                        Log.w(TAG, "Registered listener at package " + packageName + " failed, removing.");
                        listeners.remove(i);
                        i--;
                    }
                }
            }
            if (listeners.isEmpty()) {
                this.listeners.remove(packageName);
            }
        }
        if (intent != null) {
            try {
                invoker.invoke(RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER"));
            } catch (RemoteException e) {
                Log.w(TAG, "Failed to deliver message received to " + intent, e);
            }
        }
    }

    public void onPeerConnected(NodeParcelable node) {
        Log.d(TAG, "onPeerConnected: " + node);
        invokeListeners(null, listener -> listener.onPeerConnected(node));
        addConnectedNode(node);
    }

    public void onPeerDisconnected(NodeParcelable node) {
        Log.d(TAG, "onPeerDisconnected: " + node);
        try {
            getAllListeners().onPeerDisconnected(node);
        } catch (RemoteException e) {
            Log.w(TAG, e);
        }
        invokeListeners(null, listener -> listener.onPeerDisconnected(node));
        removeConnectedNode(node.getId());
    }

    public void onConnectedNodes(List<NodeParcelable> nodes) {
        Log.d(TAG, "onConnectedNodes: " + nodes);
        try {
            getAllListeners().onConnectedNodes(nodes);
        } catch (RemoteException e) {
            Log.w(TAG, e);
        }
        invokeListeners(null, listener -> listener.onConnectedNodes(nodes));
    }

    public DataItemRecord putData(PutDataRequest request, String packageName) {
@@ -444,11 +460,6 @@ public class WearableImpl {

    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());
        }
        dataHolderItems.moveToFirst();
        dataHolderItems.moveToPrevious();
        return new DataHolder(dataHolderItems, 0, null);
    }

@@ -467,40 +478,26 @@ public class WearableImpl {
            return null;
        }
        Cursor dataHolderItems = nodeDatabase.getDataItemsForDataHolderByHostAndPath(packageName, firstSignature, fixHost(uri.getHost(), false), uri.getPath());
        maybeDebugCursor("getDataItems",dataHolderItems);
        dataHolderItems.moveToFirst();
        dataHolderItems.moveToPrevious();
        DataHolder dataHolder = new DataHolder(dataHolderItems, 0, null);
        Log.d(TAG, "Returning data holder of size " + dataHolder.getCount() + " for query " + uri);
        return dataHolder;
    }

    @TargetApi(11)
    private void maybeDebugCursor(String what, Cursor cursor) {
        if (SDK_INT >= 11) {
            int j = 0;
            while (cursor.moveToNext()) {
                for (int i = 0; i < cursor.getColumnCount(); i++) {
                    if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING) {
                        Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getString(i));
                    }
                    if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER)
                        Log.d(TAG, what+"[" + j + "]: " + cursor.getColumnName(i) + "=" + cursor.getLong(i));
                }
            }
        }
    }

    public synchronized void addListener(String packageName, IWearableListener listener) {
    public synchronized void addListener(String packageName, IWearableListener listener, IntentFilter[] filters) {
        if (!listeners.containsKey(packageName)) {
            listeners.put(packageName, new ArrayList<IWearableListener>());
            listeners.put(packageName, new ArrayList<ListenerInfo>());
        }
        listeners.get(packageName).add(listener);
        listeners.get(packageName).add(new ListenerInfo(listener, filters));
    }

    public void removeListener(IWearableListener listener) {
        for (List<IWearableListener> list : listeners.values()) {
            list.remove(listener);
        for (List<ListenerInfo> list : listeners.values()) {
            for (int i = 0; i < list.size(); i++) {
                if (list.get(i).listener.equals(listener)) {
                    list.remove(i);
                    i--;
                }
            }
        }
    }

@@ -546,12 +543,10 @@ public class WearableImpl {

    public void sendMessageReceived(String packageName, MessageEventParcelable messageEvent) {
        Log.d(TAG, "onMessageReceived: " + messageEvent);
        try {
            getListener(packageName, "com.google.android.gms.wearable.MESSAGE_RECEIVED", Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath()))
                    .onMessageReceived(messageEvent);
        } catch (RemoteException e) {
            Log.w(TAG, e);
        }
        Intent intent = new Intent("com.google.android.gms.wearable.MESSAGE_RECEIVED");
        intent.setPackage(packageName);
        intent.setData(Uri.parse("wear://" + getLocalNodeId() + "/" + messageEvent.getPath()));
        invokeListeners(intent, listener -> listener.onMessageReceived(messageEvent));
    }

    public DataItemRecord getDataItemByUri(Uri uri, String packageName) {
@@ -568,17 +563,11 @@ public class WearableImpl {
    }

    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);
        }
        return RemoteListenerProxy.get(context, intent, IWearableListener.class, "com.google.android.gms.wearable.BIND_LISTENER");
    }

    private void closeConnection(String nodeId) {
@@ -594,7 +583,7 @@ public class WearableImpl {
        }
        activeConnections.remove(nodeId);
        for (ConnectionConfiguration config : getConfigurations()) {
            if (config.nodeId.equals(nodeId) || config.peerNodeId.equals(nodeId)) {
            if (nodeId.equals(config.nodeId) || nodeId.equals(config.peerNodeId)) {
                config.connected = false;
            }
        }
@@ -631,4 +620,14 @@ public class WearableImpl {
    public void stop() {
        this.networkHandler.getLooper().quit();
    }

    private class ListenerInfo {
        private IWearableListener listener;
        private IntentFilter[] filters;

        private ListenerInfo(IWearableListener listener, IntentFilter[] filters) {
            this.listener = listener;
            this.filters = filters;
        }
    }
}
+44 −12
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013-2017 microG Project Team
 * Copyright (C) 2013-2019 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.
@@ -29,8 +29,11 @@ import com.google.android.gms.common.api.Status;
import com.google.android.gms.wearable.Asset;
import com.google.android.gms.wearable.ConnectionConfiguration;
import com.google.android.gms.wearable.internal.AddListenerRequest;
import com.google.android.gms.wearable.internal.AddLocalCapabilityResponse;
import com.google.android.gms.wearable.internal.AncsNotificationParcelable;
import com.google.android.gms.wearable.internal.CapabilityInfoParcelable;
import com.google.android.gms.wearable.internal.DeleteDataItemsResponse;
import com.google.android.gms.wearable.internal.GetCapabilityResponse;
import com.google.android.gms.wearable.internal.GetCloudSyncSettingResponse;
import com.google.android.gms.wearable.internal.GetConfigResponse;
import com.google.android.gms.wearable.internal.GetConfigsResponse;
@@ -45,9 +48,13 @@ 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 com.google.android.gms.wearable.internal.RemoveLocalCapabilityResponse;
import com.google.android.gms.wearable.internal.SendMessageResponse;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class WearableServiceImpl extends IWearableService.Stub {
    private static final String TAG = "GmsWearSvcImpl";
@@ -56,11 +63,13 @@ public class WearableServiceImpl extends IWearableService.Stub {
    private final String packageName;
    private final WearableImpl wearable;
    private final Handler mainHandler;
    private final CapabilityManager capabilities;

    public WearableServiceImpl(Context context, WearableImpl wearable, String packageName) {
        this.context = context;
        this.wearable = wearable;
        this.packageName = packageName;
        this.capabilities = new CapabilityManager(context, wearable, packageName);
        this.mainHandler = new Handler(context.getMainLooper());
    }

@@ -191,8 +200,11 @@ public class WearableServiceImpl extends IWearableService.Stub {
    @Override
    public void deleteDataItemsWithFilter(IWearableCallbacks callbacks, final Uri uri, int typeFilter) throws RemoteException {
        Log.d(TAG, "deleteDataItems: " + uri);
        postMain(callbacks, () -> {
        this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
            @Override
            public void run(IWearableCallbacks callbacks) throws RemoteException {
                callbacks.onDeleteDataItemsResponse(new DeleteDataItemsResponse(0, wearable.deleteDataItems(uri, packageName)));
            }
        });
    }

@@ -289,29 +301,49 @@ public class WearableServiceImpl extends IWearableService.Stub {
     */

    @Override
    public void getConnectedCapability(IWearableCallbacks callbacks, String s1, int i) throws RemoteException {
        Log.d(TAG, "unimplemented Method: getConnectedCapability " + s1 + ", " + i);
    public void getConnectedCapability(IWearableCallbacks callbacks, String capability, int nodeFilter) throws RemoteException {
        Log.d(TAG, "unimplemented Method: getConnectedCapability " + capability + ", " + nodeFilter);
        postMain(callbacks, () -> {
            List<NodeParcelable> nodes = new ArrayList<>();
            for (String host : capabilities.getNodesForCapability(capability)) {
                nodes.add(new NodeParcelable(host, host));
            }
            CapabilityInfoParcelable capabilityInfo = new CapabilityInfoParcelable(capability, nodes);
            callbacks.onGetCapabilityResponse(new GetCapabilityResponse(0, capabilityInfo));
        });
    }

    @Override
    public void getConnectedCapaibilties(IWearableCallbacks callbacks, int i) throws RemoteException {
        Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + i);
    public void getConnectedCapaibilties(IWearableCallbacks callbacks, int nodeFilter) throws RemoteException {
        Log.d(TAG, "unimplemented Method: getConnectedCapaibilties: " + nodeFilter);
    }

    @Override
    public void addLocalCapability(IWearableCallbacks callbacks, String cap) throws RemoteException {
        Log.d(TAG, "unimplemented Method: addLocalCapability: " + cap);
    public void addLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
        Log.d(TAG, "unimplemented Method: addLocalCapability: " + capability);
        this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
            @Override
            public void run(IWearableCallbacks callbacks) throws RemoteException {
                callbacks.onAddLocalCapabilityResponse(new AddLocalCapabilityResponse(capabilities.add(capability)));
            }
        });
    }

    @Override
    public void removeLocalCapability(IWearableCallbacks callbacks, String cap) throws RemoteException {
        Log.d(TAG, "unimplemented Method: removeLocalCapability: " + cap);
    public void removeLocalCapability(IWearableCallbacks callbacks, String capability) throws RemoteException {
        Log.d(TAG, "unimplemented Method: removeLocalCapability: " + capability);
        this.wearable.networkHandler.post(new CallbackRunnable(callbacks) {
            @Override
            public void run(IWearableCallbacks callbacks) throws RemoteException {
                callbacks.onRemoveLocalCapabilityResponse(new RemoveLocalCapabilityResponse(capabilities.remove(capability)));
            }
        });
    }

    @Override
    public void addListener(IWearableCallbacks callbacks, AddListenerRequest request) throws RemoteException {
        if (request.listener != null) {
            wearable.addListener(packageName, request.listener);
            wearable.addListener(packageName, request.listener, request.intentFilters);
        }
        callbacks.onStatus(Status.SUCCESS);
    }