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

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

Merge "Re-register Context Hub Service hub on HAL restart" into main

parents 451f0f5b 9a516362
Loading
Loading
Loading
Loading
+51 −25
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
@@ -92,7 +94,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
    private final ScheduledExecutorService mSessionTimeoutExecutor;

    /** The proxy to talk to the Context Hub HAL for endpoint communication. */
    private final IEndpointCommunication mHubInterface;
    @GuardedBy("mRegistrationLock")
    private IEndpointCommunication mHubInterface;

    /** The manager that registered this endpoint. */
    private final ContextHubEndpointManager mEndpointManager;
@@ -257,7 +260,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub

    /* package */ ContextHubEndpointBroker(
            Context context,
            IEndpointCommunication hubInterface,
            @NonNull IEndpointCommunication hubInterface,
            ContextHubEndpointManager endpointManager,
            EndpointInfo halEndpointInfo,
            @NonNull IContextHubEndpointCallback callback,
@@ -311,8 +314,12 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
        synchronized (mOpenSessionLock) {
            try {
                mSessionMap.put(sessionId, new Session(destination, false));
                mHubInterface.openEndpointSession(
                        sessionId, halEndpointInfo.id, mHalEndpointInfo.id, serviceDescriptor);
                getHubInterface()
                        .openEndpointSession(
                                sessionId,
                                halEndpointInfo.id,
                                mHalEndpointInfo.id,
                                serviceDescriptor);
            } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
                Log.e(TAG, "Exception while calling HAL openEndpointSession", e);
                cleanupSessionResources(sessionId);
@@ -350,7 +357,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
            }
        }
        synchronized (mRegistrationLock) {
            if (!isRegistered()) {
            if (!mIsRegistered) {
                Log.w(TAG, "Attempting to unregister when already unregistered");
                return;
            }
@@ -360,8 +367,8 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException while calling HAL unregisterEndpoint", e);
            }
        }
            mEndpointManager.unregisterEndpoint(mEndpointInfo.getIdentifier().getEndpoint());
        }
        releaseWakeLockOnExit();
    }

@@ -376,7 +383,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
                        "openSessionRequestComplete for invalid session id=" + sessionId);
            }
            try {
                mHubInterface.endpointSessionOpenComplete(sessionId);
                getHubInterface().endpointSessionOpenComplete(sessionId);
                info.cancelSessionOpenTimeoutFuture();
                info.setSessionState(Session.SessionState.ACTIVE);
            } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
@@ -404,7 +411,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
            Message halMessage = ContextHubServiceUtil.createHalMessage(message);
            if (callback == null) {
                try {
                    mHubInterface.sendMessageToEndpoint(sessionId, halMessage);
                    getHubInterface().sendMessageToEndpoint(sessionId, halMessage);
                } catch (RemoteException e) {
                    Log.e(
                            TAG,
@@ -438,7 +445,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
                        };
                ContextHubServiceTransaction transaction =
                        mTransactionManager.createSessionMessageTransaction(
                                mHubInterface,
                                getHubInterface(),
                                sessionId,
                                halMessage,
                                mPackageName,
@@ -469,7 +476,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
        status.messageSequenceNumber = messageSeqNumber;
        status.errorCode = errorCode;
        try {
            mHubInterface.sendMessageDeliveryStatusToEndpoint(sessionId, status);
            getHubInterface().sendMessageDeliveryStatusToEndpoint(sessionId, status);
        } catch (RemoteException e) {
            Log.w(
                    TAG,
@@ -553,14 +560,19 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
     */
    /* package */ void register() throws RemoteException {
        synchronized (mRegistrationLock) {
            if (isRegistered()) {
            registerLocked();
        }
    }

    @GuardedBy("mRegistrationLock")
    private void registerLocked() throws RemoteException {
        if (mIsRegistered) {
            Log.w(TAG, "Attempting to register when already registered");
        } else {
            mHubInterface.registerEndpoint(mHalEndpointInfo);
            mIsRegistered = true;
        }
    }
    }

    /* package */ void attachDeathRecipient() throws RemoteException {
        mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
@@ -630,20 +642,28 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
        }
    }

    /* package */ void onHalRestart() {
    /**
     * Handle the case where the underlying Context Hub HAL has restarted.
     *
     * @param hubInterface The new interface to the Context Hub HAL.
     */
    /* package */ void onHalRestart(@NonNull IEndpointCommunication hubInterface) {
        synchronized (mOpenSessionLock) {
            for (int i = mSessionMap.size() - 1; i >= 0; i--) {
                int id = mSessionMap.keyAt(i);
                onCloseEndpointSession(id, Reason.HUB_RESET);
            }
        }
        synchronized (mRegistrationLock) {
            if (mIsRegistered) {
                mIsRegistered = false;
                mHubInterface = hubInterface;
                try {
                register();
                    registerLocked();
                } catch (RemoteException e) {
                    Log.e(TAG, "RemoteException while calling HAL registerEndpoint", e);
                }
            }
        synchronized (mOpenSessionLock) {
            for (int i = mSessionMap.size() - 1; i >= 0; i--) {
                int id = mSessionMap.keyAt(i);
                onCloseEndpointSession(id, Reason.HUB_RESET);
            }
        }
    }

@@ -901,4 +921,10 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
        mEndpointManager.halCloseEndpointSessionNoThrow(sessionId, halReason);
        onCloseEndpointSession(sessionId, halReason);
    }

    private IEndpointCommunication getHubInterface() {
        synchronized (mRegistrationLock) {
            return mHubInterface;
        }
    }
}
+83 −48
Original line number Diff line number Diff line
@@ -85,8 +85,8 @@ import java.util.function.Consumer;
    private final Object mEndpointLock = new Object();

    /**
     * The next available endpoint ID to register. Per EndpointId.aidl definition, dynamic
     * endpoint IDs must have the left-most bit as 1, and the values 0/-1 are invalid.
     * The next available endpoint ID to register. Per EndpointId.aidl definition, dynamic endpoint
     * IDs must have the left-most bit as 1, and the values 0/-1 are invalid.
     */
    @GuardedBy("mEndpointLock")
    private long mNextEndpointId = -2;
@@ -100,6 +100,9 @@ import java.util.function.Consumer;
    /** Variables for managing session ID creation */
    private final Object mSessionIdLock = new Object();

    /** Variables for managing HAL restart */
    private final Object mHalRestartLock = new Object();

    /** A set of session IDs that have been reserved by an endpoint. */
    @GuardedBy("mSessionIdLock")
    private final Set<Integer> mReservedSessionIds =
@@ -109,7 +112,7 @@ import java.util.function.Consumer;
    private int mNextSessionId = 0;

    /** Set true if init() succeeds */
    private boolean mSessionIdsValid = false;
    private boolean mIsRegistered = false;

    /** The interface for endpoint communication (retrieved from HAL in init()) */
    private IEndpointCommunication mHubInterface = null;
@@ -189,14 +192,24 @@ import java.util.function.Consumer;
    /**
     * Initializes this class.
     *
     * This is separate from the constructor so that this may be passed into the callback registered
     * with the HAL.
     * <p>This is separate from the constructor so that this may be passed into the callback
     * registered with the HAL.
     *
     * @throws IllegalStateException if mHubInterface is null
     * @throws InstantiationException on unexpected failure
     * @throws UnsupportedOperationException if not supported by the HAL
     */
    /* package */ void init() throws InstantiationException, UnsupportedOperationException {
        if (mSessionIdsValid) {
    /* package */ void init()
            throws IllegalStateException, InstantiationException, UnsupportedOperationException {
        synchronized (mHalRestartLock) {
            initLocked();
        }
    }

    @GuardedBy("mHalRestartLock")
    private void initLocked()
            throws IllegalStateException, InstantiationException, UnsupportedOperationException {
        if (mIsRegistered) {
            throw new IllegalStateException("Already initialized");
        }
        try {
@@ -209,9 +222,9 @@ import java.util.function.Consumer;
            contextHubInfo.toolchain = "";
            contextHubInfo.supportedPermissions = new String[0];
            info.hubDetails = HubInfo.HubDetails.contextHubInfo(contextHubInfo);
            mHubInterface = mContextHubProxy.registerEndpointHub(
                    new ContextHubHalEndpointCallback(mHubInfoRegistry, this),
                    info);
            mHubInterface =
                    mContextHubProxy.registerEndpointHub(
                            new ContextHubHalEndpointCallback(mHubInfoRegistry, this), info);
            if (mHubInterface == null) {
                throw new IllegalStateException("Received null IEndpointCommunication");
            }
@@ -250,7 +263,7 @@ import java.util.function.Consumer;
        synchronized (mSessionIdLock) {
            mNextSessionId = mMinSessionId;
        }
        mSessionIdsValid = true;
        mIsRegistered = true;
    }

    /**
@@ -268,7 +281,8 @@ import java.util.function.Consumer;
            String packageName,
            String attributionTag)
            throws RemoteException {
        if (!mSessionIdsValid) {
        synchronized (mHalRestartLock) {
            if (!mIsRegistered) {
                throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
            }
            ContextHubEndpointBroker broker;
@@ -299,10 +313,12 @@ import java.util.function.Consumer;
                return null;
            }

        mRegistrationRecordDeque.add(new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
            mRegistrationRecordDeque.add(
                    new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
            Log.d(TAG, "Registered endpoint with ID = " + endpointId);
            return IContextHubEndpoint.Stub.asInterface(broker);
        }
    }

    /**
     * Reserves an available session ID for an endpoint.
@@ -351,6 +367,7 @@ import java.util.function.Consumer;
     * @param endpointId The ID of the endpoint to unregister.
     */
    /* package */ void unregisterEndpoint(long endpointId) {
        Log.d(TAG, "Unregistering endpoint with ID = " + endpointId);
        ContextHubEndpointBroker broker = mEndpointMap.remove(endpointId);
        if (broker != null) {
            mRegistrationRecordDeque.add(
@@ -360,9 +377,25 @@ import java.util.function.Consumer;

    /** Invoked by the service when the Context Hub HAL restarts. */
    /* package */ void onHalRestart() {
        synchronized (mHalRestartLock) {
            Log.d(TAG, "onHalRestart");
            mIsRegistered = false;
            try {
                initLocked();
            } catch (IllegalStateException
                    | InstantiationException
                    | UnsupportedOperationException e) {
                Log.e(TAG, "Failed to re-register ContextHubService:" + e.getMessage());
            }

            for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
            // The broker will close existing sessions and re-register itself
            broker.onHalRestart();
                if (mIsRegistered) {
                    // mHubInterface is guaranteed to be valid if mIsRegistered is true
                    broker.onHalRestart(mHubInterface);
                } else {
                    broker.unregister();
                }
            }
        }
    }

@@ -541,7 +574,9 @@ import java.util.function.Consumer;
        }
    }

    /** @return an available endpoint ID */
    /**
     * @return an available endpoint ID
     */
    private long getNewEndpointId() {
        synchronized (mEndpointLock) {
            if (mNextEndpointId >= 0) {
+38 −3
Original line number Diff line number Diff line
@@ -105,6 +105,8 @@ public class ContextHubEndpointTest {
    @Mock private IContextHubEndpointCallback mMockCallback;
    @Rule public final MockitoRule mockito = MockitoJUnit.rule();

    private int mNumHalRestarts = 0;

    @Before
    public void setUp() throws RemoteException, InstantiationException {
        when(mMockContextHubWrapper.getHubs()).thenReturn(Collections.emptyList());
@@ -170,6 +172,13 @@ public class ContextHubEndpointTest {
        assertThat(statusCaptor.getValue().errorCode).isEqualTo(ErrorCode.DESTINATION_NOT_FOUND);
    }

    private void restartHalAndVerifyHubRegistration() throws RemoteException {
        mEndpointManager.onHalRestart();
        mNumHalRestarts++;
        verify(mMockContextHubWrapper, times(mNumHalRestarts + 1))
                .registerEndpointHub(any(), any());
    }

    @Test
    public void testHalRestart() throws RemoteException {
        IContextHubEndpoint endpoint = registerExampleEndpoint();
@@ -177,7 +186,7 @@ public class ContextHubEndpointTest {
        // Verify that the endpoint is still registered after a HAL restart
        HubEndpointInfo assignedInfo = endpoint.getAssignedHubEndpointInfo();
        HubEndpointIdentifier assignedIdentifier = assignedInfo.getIdentifier();
        mEndpointManager.onHalRestart();
        restartHalAndVerifyHubRegistration();
        ArgumentCaptor<EndpointInfo> statusCaptor = ArgumentCaptor.forClass(EndpointInfo.class);
        verify(mMockEndpointCommunications, times(2)).registerEndpoint(statusCaptor.capture());
        assertThat(statusCaptor.getValue().id.id).isEqualTo(assignedIdentifier.getEndpoint());
@@ -186,6 +195,33 @@ public class ContextHubEndpointTest {
        unregisterExampleEndpoint(endpoint);
    }

    @Test
    public void testHalRestartCanOpenSession() throws RemoteException {
        IContextHubEndpoint endpoint = registerExampleEndpoint();

        // Verify that the endpoint is still registered after a HAL restart
        HubEndpointInfo assignedInfo = endpoint.getAssignedHubEndpointInfo();
        HubEndpointIdentifier assignedIdentifier = assignedInfo.getIdentifier();
        restartHalAndVerifyHubRegistration();
        ArgumentCaptor<EndpointInfo> statusCaptor = ArgumentCaptor.forClass(EndpointInfo.class);
        verify(mMockEndpointCommunications, times(2)).registerEndpoint(statusCaptor.capture());
        assertThat(statusCaptor.getValue().id.id).isEqualTo(assignedIdentifier.getEndpoint());
        assertThat(statusCaptor.getValue().id.hubId).isEqualTo(assignedIdentifier.getHub());

        // Verify that the endpoint can open a session after a HAL restart
        HubEndpointInfo targetInfo =
                new HubEndpointInfo(
                        TARGET_ENDPOINT_NAME,
                        TARGET_ENDPOINT_ID,
                        ENDPOINT_PACKAGE_NAME,
                        Collections.emptyList());
        int sessionId = endpoint.openSession(targetInfo, /* serviceDescriptor= */ null);
        mEndpointManager.onEndpointSessionOpenComplete(sessionId);
        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE - 1);

        unregisterExampleEndpoint(endpoint);
    }

    @Test
    public void testHalRestartOnOpenSession() throws RemoteException {
        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
@@ -201,8 +237,7 @@ public class ContextHubEndpointTest {
        mEndpointManager.onEndpointSessionOpenComplete(sessionId);
        assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE - 1);

        mEndpointManager.onHalRestart();

        restartHalAndVerifyHubRegistration();
        HubEndpointInfo assignedInfo = endpoint.getAssignedHubEndpointInfo();
        HubEndpointIdentifier assignedIdentifier = assignedInfo.getIdentifier();
        ArgumentCaptor<EndpointInfo> statusCaptor = ArgumentCaptor.forClass(EndpointInfo.class);