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

Commit 3e5930bf authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implements endpoint discovery callback" into main

parents 7bb35ae7 01f6292c
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);