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

Commit 01f6292c authored by Arthur Ishiguro's avatar Arthur Ishiguro
Browse files

Implements endpoint discovery callback

Bug: 381102105
Flag: android.chre.flags.offload_implementation
Test: Compile
Change-Id: I24c6fa5ee0ee83d4dea458ca99e63928b7a237b5
parent 29925d01
Loading
Loading
Loading
Loading
+14 −4
Original line number Diff line number Diff line
@@ -798,7 +798,7 @@ public class ContextHubService extends IContextHubService.Stub {
            throws RemoteException {
        super.registerEndpoint_enforcePermission();
        if (mEndpointManager == null) {
            Log.e(TAG, "ContextHubService.registerEndpoint: endpoint manager failed to initialize");
            Log.e(TAG, "Endpoint manager failed to initialize");
            throw new UnsupportedOperationException("Endpoint registration is not supported");
        }
        return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
@@ -809,7 +809,8 @@ public class ContextHubService extends IContextHubService.Stub {
    public void registerEndpointDiscoveryCallbackId(
            long endpointId, IContextHubEndpointDiscoveryCallback callback) throws RemoteException {
        super.registerEndpointDiscoveryCallbackId_enforcePermission();
        // TODO(b/375487784): Implement this
        checkEndpointDiscoveryPreconditions();
        mHubInfoRegistry.registerEndpointDiscoveryCallback(endpointId, callback);
    }

    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -818,7 +819,8 @@ public class ContextHubService extends IContextHubService.Stub {
            String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback)
            throws RemoteException {
        super.registerEndpointDiscoveryCallbackDescriptor_enforcePermission();
        // TODO(b/375487784): Implement this
        checkEndpointDiscoveryPreconditions();
        mHubInfoRegistry.registerEndpointDiscoveryCallback(serviceDescriptor, callback);
    }

    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -826,7 +828,15 @@ public class ContextHubService extends IContextHubService.Stub {
    public void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback)
            throws RemoteException {
        super.unregisterEndpointDiscoveryCallback_enforcePermission();
        // TODO(b/375487784): Implement this
        checkEndpointDiscoveryPreconditions();
        mHubInfoRegistry.unregisterEndpointDiscoveryCallback(callback);
    }

    private void checkEndpointDiscoveryPreconditions() {
        if (mHubInfoRegistry == null) {
            Log.e(TAG, "Hub endpoint registry failed to initialize");
            throw new UnsupportedOperationException("Endpoint discovery is not supported");
        }
    }

    /**
+161 −1
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.server.location.contexthub;

import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback;
import android.hardware.location.HubInfo;
import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
@@ -29,6 +31,9 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;

class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycleCallback {
    private static final String TAG = "HubInfoRegistry";
@@ -43,6 +48,56 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
    private final ArrayMap<HubEndpointInfo.HubEndpointIdentifier, HubEndpointInfo>
            mHubEndpointInfos = new ArrayMap<>();

    /**
     * A wrapper class that is used to store arguments to
     * ContextHubManager.registerEndpointCallback.
     */
    private static class DiscoveryCallback {
        private final IContextHubEndpointDiscoveryCallback mCallback;
        private final Optional<Long> mEndpointId;
        private final Optional<String> mServiceDescriptor;

        DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, long endpointId) {
            mCallback = callback;
            mEndpointId = Optional.of(endpointId);
            mServiceDescriptor = Optional.empty();
        }

        DiscoveryCallback(IContextHubEndpointDiscoveryCallback callback, String serviceDescriptor) {
            mCallback = callback;
            mEndpointId = Optional.empty();
            mServiceDescriptor = Optional.of(serviceDescriptor);
        }

        public IContextHubEndpointDiscoveryCallback getCallback() {
            return mCallback;
        }

        /**
         * @param info The hub endpoint info to check
         * @return true if info matches
         */
        public boolean isMatch(HubEndpointInfo info) {
            if (mEndpointId.isPresent()) {
                return mEndpointId.get() == info.getIdentifier().getEndpoint();
            }
            if (mServiceDescriptor.isPresent()) {
                for (HubServiceInfo serviceInfo : info.getServiceInfoCollection()) {
                    if (mServiceDescriptor.get().equals(serviceInfo.getServiceDescriptor())) {
                        return true;
                    }
                }
            }
            return false;
        }
    }

    /* The list of discovery callbacks registered with the service */
    @GuardedBy("mCallbackLock")
    private final List<DiscoveryCallback> mEndpointDiscoveryCallbacks = new ArrayList<>();

    private final Object mCallbackLock = new Object();

    HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
        mContextHubWrapper = contextHubWrapper;
        refreshCachedHubs();
@@ -109,16 +164,50 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
                mHubEndpointInfos.put(endpointInfo.getIdentifier(), endpointInfo);
            }
        }

        invokeForMatchingEndpoints(
                endpointInfos,
                (cb, infoList) -> {
                    try {
                        cb.onEndpointsStarted(infoList);
                    } catch (RemoteException e) {
                        if (e instanceof DeadObjectException) {
                            Log.w(TAG, "onEndpointStarted: callback died, unregistering");
                            unregisterEndpointDiscoveryCallback(cb);
                        } else {
                            Log.e(TAG, "Exception while calling onEndpointsStarted", e);
                        }
                    }
                });
    }

    @Override
    public void onEndpointStopped(
            HubEndpointInfo.HubEndpointIdentifier[] endpointIds, byte reason) {
        ArrayList<HubEndpointInfo> removedInfoList = new ArrayList<>();
        synchronized (mLock) {
            for (HubEndpointInfo.HubEndpointIdentifier endpointId : endpointIds) {
                mHubEndpointInfos.remove(endpointId);
                HubEndpointInfo info = mHubEndpointInfos.remove(endpointId);
                if (info != null) {
                    removedInfoList.add(info);
                }
            }
        }

        invokeForMatchingEndpoints(
                removedInfoList.toArray(new HubEndpointInfo[removedInfoList.size()]),
                (cb, infoList) -> {
                    try {
                        cb.onEndpointsStopped(infoList, reason);
                    } catch (RemoteException e) {
                        if (e instanceof DeadObjectException) {
                            Log.w(TAG, "onEndpointStopped: callback died, unregistering");
                            unregisterEndpointDiscoveryCallback(cb);
                        } else {
                            Log.e(TAG, "Exception while calling onEndpointsStopped", e);
                        }
                    }
                });
    }

    /** Return a list of {@link HubEndpointInfo} that represents endpoints with the matching id. */
@@ -151,6 +240,77 @@ class HubInfoRegistry implements ContextHubHalEndpointCallback.IEndpointLifecycl
        return searchResult;
    }

    /* package */
    void registerEndpointDiscoveryCallback(
            long endpointId, IContextHubEndpointDiscoveryCallback callback) {
        Objects.requireNonNull(callback, "callback cannot be null");
        synchronized (mCallbackLock) {
            checkCallbackAlreadyRegistered(callback);
            mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, endpointId));
        }
    }

    /* package */
    void registerEndpointDiscoveryCallback(
            String serviceDescriptor, IContextHubEndpointDiscoveryCallback callback) {
        Objects.requireNonNull(callback, "callback cannot be null");
        synchronized (mCallbackLock) {
            checkCallbackAlreadyRegistered(callback);
            mEndpointDiscoveryCallbacks.add(new DiscoveryCallback(callback, serviceDescriptor));
        }
    }

    /* package */
    void unregisterEndpointDiscoveryCallback(IContextHubEndpointDiscoveryCallback callback) {
        Objects.requireNonNull(callback, "callback cannot be null");
        synchronized (mCallbackLock) {
            for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
                if (discoveryCallback.getCallback().asBinder() == callback.asBinder()) {
                    mEndpointDiscoveryCallbacks.remove(discoveryCallback);
                    break;
                }
            }
        }
    }

    private void checkCallbackAlreadyRegistered(
            IContextHubEndpointDiscoveryCallback callback) {
        synchronized (mCallbackLock) {
            for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
                if (discoveryCallback.mCallback.asBinder() == callback.asBinder()) {
                    throw new IllegalArgumentException("Callback is already registered");
                }
            }
        }
    }

    /**
     * Iterates through all registered discovery callbacks and invokes a given callback for those
     * that match the endpoints the callback is targeted for.
     *
     * @param endpointInfos The list of endpoint infos to check for a match.
     * @param consumer The callback to invoke, which consumes the callback object and the list of
     *     matched endpoint infos.
     */
    private void invokeForMatchingEndpoints(
            HubEndpointInfo[] endpointInfos,
            BiConsumer<IContextHubEndpointDiscoveryCallback, HubEndpointInfo[]> consumer) {
        synchronized (mCallbackLock) {
            for (DiscoveryCallback discoveryCallback : mEndpointDiscoveryCallbacks) {
                ArrayList<HubEndpointInfo> infoList = new ArrayList<>();
                for (HubEndpointInfo endpointInfo : endpointInfos) {
                    if (discoveryCallback.isMatch(endpointInfo)) {
                        infoList.add(endpointInfo);
                    }
                }

                consumer.accept(
                        discoveryCallback.getCallback(),
                        infoList.toArray(new HubEndpointInfo[infoList.size()]));
            }
        }
    }

    void dump(IndentingPrintWriter ipw) {
        synchronized (mLock) {
            dumpLocked(ipw);