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

Commit fb9e4c7a authored by Arthur Ishiguro's avatar Arthur Ishiguro
Browse files

Creates a NanoAppStateManager at ContextHubService

Created to decouple the internal nanoapp state management at the
service with ContextHubService class

Bug: 67734082
Test: Compile and flash, run CHQTS and verify pass, also verify with
logs that the cache is updated accordingly after load/unload/query
Change-Id: Ie25460e7f8d903cdcb22f0a1c4870bb93ccbad3e
parent 5930ce33
Loading
Loading
Loading
Loading
+25 −14
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.hardware.location;


import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -26,29 +25,42 @@ import android.os.Parcelable;
import libcore.util.EmptyArray;

/**
 * Describes an instance of a nanoapp, used by the internal state manged by ContextHubService.
 *
 * TODO(b/69270990) Remove this class once the old API is deprecated.
 * TODO(b/70624255) Clean up toString() by removing unnecessary fields
 *
 * @hide
 */
@SystemApi
public class NanoAppInstanceInfo {
    private String mPublisher;
    private String mName;
    private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
    private String mPublisher = PRE_LOADED_GENERIC_UNKNOWN;
    private String mName = PRE_LOADED_GENERIC_UNKNOWN;

    private int mHandle;
    private long mAppId;
    private int mAppVersion;
    private int mContexthubId;

    private int mNeededReadMemBytes;
    private int mNeededWriteMemBytes;
    private int mNeededExecMemBytes;

    private int[] mNeededSensors;
    private int[] mOutputEvents;
    private int mNeededReadMemBytes = 0;
    private int mNeededWriteMemBytes = 0;
    private int mNeededExecMemBytes = 0;

    private int mContexthubId;
    private int mHandle;
    private int[] mNeededSensors = EmptyArray.INT;
    private int[] mOutputEvents = EmptyArray.INT;

    public NanoAppInstanceInfo() {
        mNeededSensors = EmptyArray.INT;
        mOutputEvents = EmptyArray.INT;
    }

    /**
     * @hide
     */
    public NanoAppInstanceInfo(int handle, long appId, int appVersion, int contextHubId) {
        mHandle = handle;
        mAppId = appId;
        mAppVersion = appVersion;
        mContexthubId = contextHubId;
    }

    /**
@@ -282,7 +294,6 @@ public class NanoAppInstanceInfo {
        mHandle = handle;
    }


    private NanoAppInstanceInfo(Parcel in) {
        mPublisher = in.readString();
        mName = in.readString();
+25 −136
Original line number Diff line number Diff line
@@ -55,7 +55,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @hide
@@ -76,25 +75,10 @@ public class ContextHubService extends IContextHubService.Stub {
    public static final int MSG_QUERY_MEMORY = 6;
    public static final int MSG_HUB_RESET = 7;

    private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
    private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
    private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
    private static final int PRE_LOADED_APP_MEM_REQ = 0;

    private static final int OS_APP_INSTANCE = -1;

    private final Context mContext;

    // TODO(b/69270990): Remove once old ContextHubManager API is deprecated
    // Service cache maintaining of instance ID to nanoapp infos
    private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash =
            new ConcurrentHashMap<>();
    // The next available instance ID (managed by the service) to assign to a nanoapp
    private int mNextAvailableInstanceId = 0;
    // A map of the long nanoapp ID to instance ID managed by the service
    private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap =
            new ConcurrentHashMap<>();

    private final ContextHubInfo[] mContextHubInfo;
    private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
            new RemoteCallbackList<>();
@@ -111,6 +95,9 @@ public class ContextHubService extends IContextHubService.Stub {
    // The default client for old API clients
    private final Map<Integer, IContextHubClient> mDefaultClientMap;

    // The manager for the internal nanoapp state cache
    private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();

    /**
     * Class extending the callback to register with a Context Hub.
     */
@@ -160,7 +147,8 @@ public class ContextHubService extends IContextHubService.Stub {
        }

        mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
        mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager);
        mTransactionManager = new ContextHubTransactionManager(
                mContextHubProxy, mClientManager, mNanoAppStateManager);

        List<ContextHub> hubList;
        try {
@@ -214,9 +202,8 @@ public class ContextHubService extends IContextHubService.Stub {
        return new IContextHubClientCallback.Stub() {
            @Override
            public void onMessageFromNanoApp(NanoAppMessage message) {
                int nanoAppInstanceId =
                        mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ?
                        mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1;
                int nanoAppInstanceId = mNanoAppStateManager.getNanoAppInstanceId(
                        contextHubId, message.getNanoAppId());

                onMessageReceiptOldApi(
                        message.getMessageType(), contextHubId, nanoAppInstanceId,
@@ -328,15 +315,13 @@ public class ContextHubService extends IContextHubService.Stub {
     * Creates an internal unload transaction callback to be used for old API clients
     *
     * @param contextHubId the ID of the hub to unload the nanoapp
     * @param nanoAppId    the ID of the nanoapp to unload
     * @return the callback interface
     */
    private IContextHubTransactionCallback createUnloadTransactionCallback(
            int contextHubId, long nanoAppId) {
    private IContextHubTransactionCallback createUnloadTransactionCallback(int contextHubId) {
        return new IContextHubTransactionCallback.Stub() {
            @Override
            public void onTransactionComplete(int result) {
                handleUnloadResponseOldApi(contextHubId, result, nanoAppId);
                handleUnloadResponseOldApi(contextHubId, result);
            }

            @Override
@@ -424,7 +409,8 @@ public class ContextHubService extends IContextHubService.Stub {
            return -1;
        }

        NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle);
        NanoAppInstanceInfo info =
                mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppInstanceHandle);
        if (info == null) {
            Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle);
            return -1; //means failed
@@ -433,7 +419,7 @@ public class ContextHubService extends IContextHubService.Stub {
        int contextHubId = info.getContexthubId();
        long nanoAppId = info.getAppId();
        IContextHubTransactionCallback onCompleteCallback =
                createUnloadTransactionCallback(contextHubId, nanoAppId);
                createUnloadTransactionCallback(contextHubId);
        ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
                contextHubId, nanoAppId, onCompleteCallback);

@@ -448,29 +434,21 @@ public class ContextHubService extends IContextHubService.Stub {
    }

    @Override
    public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
            throws RemoteException {
    public NanoAppInstanceInfo getNanoAppInstanceInfo(
            int nanoAppInstanceHandle) throws RemoteException {
        checkPermissions();
        // This assumes that all the nanoAppInfo is current. This is reasonable
        // for the use cases for tightly controlled nanoApps.
        if (mNanoAppHash.containsKey(nanoAppInstanceHandle)) {
            return mNanoAppHash.get(nanoAppInstanceHandle);
        } else {
            Log.e(TAG, "Could not find nanoApp with handle " + nanoAppInstanceHandle);
            return null;
        }

        return mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppInstanceHandle);
    }

    @Override
    public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) throws RemoteException {
        checkPermissions();
        ArrayList<Integer> foundInstances = new ArrayList<Integer>();

        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
            NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);

        ArrayList<Integer> foundInstances = new ArrayList<>();
        for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
            if (filter.testMatch(info)) {
                foundInstances.add(nanoAppInstance);
                foundInstances.add(info.getHandle());
            }
        }

@@ -576,25 +554,10 @@ public class ContextHubService extends IContextHubService.Stub {
            return;
        }

        // NOTE: The legacy JNI code used to do a query right after a load success
        // to synchronize the service cache. Instead store the binary that was requested to
        // load to update the cache later without doing a query.
        int instanceId = 0;
        long nanoAppId = nanoAppBinary.getNanoAppId();
        int nanoAppVersion = nanoAppBinary.getNanoAppVersion();
        if (result == TransactionResult.SUCCESS) {
            if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
                instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
            } else {
                instanceId = mNextAvailableInstanceId++;
                mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
            }

            addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion);
        }

        byte[] data = new byte[5];
        data[0] = (byte) result;
        int instanceId = mNanoAppStateManager.getNanoAppInstanceId(
                contextHubId, nanoAppBinary.getNanoAppId());
        ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId);

        onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
@@ -605,14 +568,7 @@ public class ContextHubService extends IContextHubService.Stub {
     *
     * TODO(b/69270990): Remove this once the old APIs are obsolete.
     */
    private void handleUnloadResponseOldApi(
            int contextHubId, int result, long nanoAppId) {
        if (result == TransactionResult.SUCCESS) {
            int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
            deleteAppInstance(instanceId);
            mNanoAppIdToInstanceMap.remove(nanoAppId);
        }

    private void handleUnloadResponseOldApi(int contextHubId, int result) {
        byte[] data = new byte[1];
        data[0] = (byte) result;
        onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
@@ -668,41 +624,10 @@ public class ContextHubService extends IContextHubService.Stub {
        List<NanoAppState> nanoAppStateList =
                ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);

        updateServiceCache(contextHubId, nanoAppInfoList);
        mNanoAppStateManager.updateCache(contextHubId, nanoAppInfoList);
        mTransactionManager.onQueryResponse(nanoAppStateList);
    }

    /**
     * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response.
     *
     * TODO(b/69270990): Remove this when the old API functionality is removed.
     *
     * @param contextHubId    the ID of the hub the response came from
     * @param nanoAppInfoList the list of loaded nanoapps
     */
    private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
        synchronized (mNanoAppHash) {
            for (int instanceId : mNanoAppHash.keySet()) {
                if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) {
                    deleteAppInstance(instanceId);
                }
            }

            for (HubAppInfo appInfo : nanoAppInfoList) {
                int instanceId;
                long nanoAppId = appInfo.appId;
                if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) {
                    instanceId = mNanoAppIdToInstanceMap.get(nanoAppId);
                } else {
                    instanceId = mNextAvailableInstanceId++;
                    mNanoAppIdToInstanceMap.put(nanoAppId, instanceId);
                }

                addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version);
            }
        }
    }

    /**
     * @param contextHubId the hub ID to validate
     * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
@@ -833,8 +758,8 @@ public class ContextHubService extends IContextHubService.Stub {
        pw.println("");
        pw.println("=================== NANOAPPS ====================");
        // Dump nanoAppHash
        for (Integer nanoAppInstance : mNanoAppHash.keySet()) {
            pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
        for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
            pw.println(info);
        }

        // dump eventLog
@@ -908,40 +833,4 @@ public class ContextHubService extends IContextHubService.Stub {

        return true;
    }

    private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) {
        // App Id encodes vendor & version
        NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo();

        appInfo.setAppId(appId);
        appInfo.setAppVersion(appVersion);
        appInfo.setName(PRE_LOADED_APP_NAME);
        appInfo.setContexthubId(hubHandle);
        appInfo.setHandle(appInstanceHandle);
        appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER);
        appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ);
        appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
        appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);

        String action;
        if (mNanoAppHash.containsKey(appInstanceHandle)) {
            action = "Updated";
        } else {
            action = "Added";
        }

        mNanoAppHash.put(appInstanceHandle, appInfo);
        Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x"
                + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion));

        return 0;
    }

    private int deleteAppInstance(int appInstanceHandle) {
        if (mNanoAppHash.remove(appInstanceHandle) == null) {
            return -1;
        }

        return 0;
    }
}
+19 −1
Original line number Diff line number Diff line
@@ -62,6 +62,11 @@ import java.util.concurrent.atomic.AtomicInteger;
     */
    private final ContextHubClientManager mClientManager;

    /*
     * The nanoapp state manager for the service
     */
    private final NanoAppStateManager mNanoAppStateManager;

    /*
     * A queue containing the current transactions
     */
@@ -79,9 +84,11 @@ import java.util.concurrent.atomic.AtomicInteger;
    private ScheduledFuture<?> mTimeoutFuture = null;

    /* package */ ContextHubTransactionManager(
            IContexthub contextHubProxy, ContextHubClientManager clientManager) {
            IContexthub contextHubProxy, ContextHubClientManager clientManager,
            NanoAppStateManager nanoAppStateManager) {
        mContextHubProxy = contextHubProxy;
        mClientManager = clientManager;
        mNanoAppStateManager = nanoAppStateManager;
    }

    /**
@@ -113,6 +120,14 @@ import java.util.concurrent.atomic.AtomicInteger;

            @Override
            /* package */ void onTransactionComplete(int result) {
                if (result == TransactionResult.SUCCESS) {
                    // NOTE: The legacy JNI code used to do a query right after a load success
                    // to synchronize the service cache. Instead store the binary that was
                    // requested to load to update the cache later without doing a query.
                    mNanoAppStateManager.addNanoAppInstance(
                            contextHubId, nanoAppBinary.getNanoAppId(),
                            nanoAppBinary.getNanoAppVersion());
                }
                try {
                    onCompleteCallback.onTransactionComplete(result);
                    if (result == Result.OK) {
@@ -151,6 +166,9 @@ import java.util.concurrent.atomic.AtomicInteger;

            @Override
            /* package */ void onTransactionComplete(int result) {
                if (result == TransactionResult.SUCCESS) {
                    mNanoAppStateManager.removeNanoAppInstance(contextHubId, nanoAppId);
                }
                try {
                    onCompleteCallback.onTransactionComplete(result);
                    if (result == Result.OK) {
+190 −0
Original line number Diff line number Diff line
/*
 * Copyright 2017 The Android Open Source Project
 *
 * 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 com.android.server.location;

import android.annotation.Nullable;
import android.hardware.contexthub.V1_0.HubAppInfo;
import android.hardware.location.NanoAppInstanceInfo;
import android.util.Log;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * Manages the state of loaded nanoapps at the Context Hubs.
 *
 * This class maintains a list of nanoapps that have been informed as loaded at the hubs. The state
 * should be updated based on the hub callbacks (defined in IContexthubCallback.hal), as a result
 * of either loadNanoApp, unloadNanoApp, or queryApps.
 *
 * The state tracked by this manager is used by clients of ContextHubService that use the old APIs.
 *
 * TODO(b/69270990): Remove this class and its logic once the old API is deprecated.
 *
 * @hide
 */
/* package */ class NanoAppStateManager {
    private static final String TAG = "NanoAppStateManager";

    /*
     * Enables verbose debug logs for this class.
     */
    private static final boolean ENABLE_LOG_DEBUG = true;

    /*
     * Service cache maintaining of instance ID to nanoapp infos.
     */
    private final HashMap<Integer, NanoAppInstanceInfo> mNanoAppHash = new HashMap<>();

    /*
     * The next instance ID to use.
     */
    private int mNextInstanceId = 0;

    /**
     * @param instanceId the instance ID of the nanoapp
     * @return the NanoAppInstanceInfo for the given nanoapp, null if the nanoapp does not exist in
     *         the cache
     */
    @Nullable
    /* package */
    synchronized NanoAppInstanceInfo getNanoAppInstanceInfo(int instanceId) {
        return mNanoAppHash.get(instanceId);
    }

    /**
     * @return a collection of NanoAppInstanceInfo objects in the cache
     */
    /* package */
    synchronized Collection<NanoAppInstanceInfo> getNanoAppInstanceInfoCollection() {
        return mNanoAppHash.values();
    }

    /**
     * @param contextHubId the ID of the hub to search for the instance
     * @param nanoAppId the unique 64-bit ID of the nanoapp
     * @return the instance ID of the nanoapp, -1 if the nanoapp is not in the cache
     */
    /* package */
    synchronized int getNanoAppInstanceId(int contextHubId, long nanoAppId) {
        for (NanoAppInstanceInfo info : mNanoAppHash.values()) {
            if (info.getContexthubId() == contextHubId && info.getAppId() == nanoAppId) {
                return info.getHandle();
            }
        }

        return -1;
    }

    /**
     * Adds a nanoapp instance to the cache.
     *
     * If the cache already contained the nanoapp, the entry is removed and a new instance ID is
     * generated.
     *
     * @param contextHubId the ID of the hub the nanoapp is loaded in
     * @param nanoAppId the unique 64-bit ID of the nanoapp
     * @param nanoAppVersion the version of the nanoapp
     */
    /* package */
    synchronized void addNanoAppInstance(int contextHubId, long nanoAppId, int nanoAppVersion) {
        removeNanoAppInstance(contextHubId, nanoAppId);
        if (mNanoAppHash.size() == Integer.MAX_VALUE) {
            Log.e(TAG, "Error adding nanoapp instance: max limit exceeded");
            return;
        }

        int instanceId = mNextInstanceId;
        for (int i = 0; i <= Integer.MAX_VALUE; i++) {
            if (!mNanoAppHash.containsKey(instanceId)) {
                mNanoAppHash.put(instanceId, new NanoAppInstanceInfo(
                        instanceId, nanoAppId, nanoAppVersion, contextHubId));
                mNextInstanceId = (instanceId == Integer.MAX_VALUE) ? 0 : instanceId + 1;
                break;
            }
            instanceId = (instanceId == Integer.MAX_VALUE) ? 0 : instanceId + 1;
        }

        if (ENABLE_LOG_DEBUG) {
            Log.v(TAG, "Added app instance " + instanceId + " to hub " + contextHubId
                    + ": ID=0x" + Long.toHexString(nanoAppId)
                    + ", version=0x" + Integer.toHexString(nanoAppVersion));
        }
    }

    /**
     * Removes a nanoapp instance from the cache.
     *
     * @param nanoAppId the ID of the nanoapp to remove the instance of
     */
    /* package */
    synchronized void removeNanoAppInstance(int contextHubId, long nanoAppId) {
        int instanceId = getNanoAppInstanceId(contextHubId, nanoAppId);
        mNanoAppHash.remove(instanceId);
    }

    /**
     * Performs a batch update of the nanoapp cache given a nanoapp query response.
     *
     * @param contextHubId    the ID of the hub the response came from
     * @param nanoAppInfoList the list of loaded nanoapps
     */
    /* package */
    synchronized void updateCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
        HashSet<Long> nanoAppIdSet = new HashSet<>();
        for (HubAppInfo appInfo : nanoAppInfoList) {
            handleQueryAppEntry(contextHubId, appInfo.appId, appInfo.version);
            nanoAppIdSet.add(appInfo.appId);
        }

        for (int instanceId : mNanoAppHash.keySet()) {
            NanoAppInstanceInfo info = mNanoAppHash.get(instanceId);
            if (info.getContexthubId() == contextHubId &&
                    !nanoAppIdSet.contains(info.getAppId())) {
                mNanoAppHash.remove(instanceId);
            }
        }
    }

    /**
     * If the nanoapp exists in the cache, then the entry is updated. Otherwise, inserts a new
     * instance of the nanoapp in the cache. This method should only be invoked from updateCache.
     *
     * @param contextHubId the ID of the hub the nanoapp is loaded in
     * @param nanoAppId the unique 64-bit ID of the nanoapp
     * @param nanoAppVersion the version of the nanoapp
     */
    private void handleQueryAppEntry(int contextHubId, long nanoAppId, int nanoAppVersion) {
        int instanceId = getNanoAppInstanceId(contextHubId, nanoAppId);
        if (instanceId == -1) {
            addNanoAppInstance(contextHubId, nanoAppId, nanoAppVersion);
        } else {
            NanoAppInstanceInfo info = mNanoAppHash.get(instanceId);
            if (info.getAppVersion() != nanoAppVersion) {
                mNanoAppHash.put(instanceId, new NanoAppInstanceInfo(
                        instanceId, nanoAppId, nanoAppVersion, contextHubId));
                if (ENABLE_LOG_DEBUG) {
                    Log.v(TAG, "Updated app instance " + instanceId + " at hub " + contextHubId
                            + ": ID=0x" + Long.toHexString(nanoAppId)
                            + ", version=0x" + Integer.toHexString(nanoAppVersion));
                }
            }
        }
    }
}