From 3fcc716bdcbf0f63f4a6c2c34338c690dd096612 Mon Sep 17 00:00:00 2001 From: Arthur Ishiguro Date: Mon, 6 Dec 2021 21:35:47 +0000 Subject: [PATCH] Expose NanoAppRpcService from nanoapps Bug: 194287786 Test: Add fake RPC service and verify through logs Change-Id: I563c6e97d300957179a380b25756ac11f66ab171 --- core/api/system-current.txt | 11 ++ .../hardware/location/NanoAppRpcService.aidl | 22 +++ .../hardware/location/NanoAppRpcService.java | 159 ++++++++++++++++++ .../hardware/location/NanoAppState.java | 69 +++++++- .../contexthub/ContextHubServiceUtil.java | 8 +- 5 files changed, 262 insertions(+), 7 deletions(-) create mode 100644 core/java/android/hardware/location/NanoAppRpcService.aidl create mode 100644 core/java/android/hardware/location/NanoAppRpcService.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a33d0a2c08795..1020517a166c1 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4363,13 +4363,24 @@ package android.hardware.location { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public final class NanoAppRpcService implements android.os.Parcelable { + ctor public NanoAppRpcService(long, int); + method public int describeContents(); + method public long getId(); + method public int getVersion(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public final class NanoAppState implements android.os.Parcelable { ctor public NanoAppState(long, int, boolean); ctor public NanoAppState(long, int, boolean, @NonNull java.util.List); + ctor public NanoAppState(long, int, boolean, @NonNull java.util.List, @NonNull java.util.List); method public int describeContents(); method public long getNanoAppId(); method @NonNull public java.util.List getNanoAppPermissions(); method public long getNanoAppVersion(); + method @NonNull public java.util.List getRpcServices(); method public boolean isEnabled(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; diff --git a/core/java/android/hardware/location/NanoAppRpcService.aidl b/core/java/android/hardware/location/NanoAppRpcService.aidl new file mode 100644 index 0000000000000..72ac8af18209f --- /dev/null +++ b/core/java/android/hardware/location/NanoAppRpcService.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2021 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 android.hardware.location; + +/** + * @hide + */ +parcelable NanoAppRpcService; diff --git a/core/java/android/hardware/location/NanoAppRpcService.java b/core/java/android/hardware/location/NanoAppRpcService.java new file mode 100644 index 0000000000000..4b8f0580fe31d --- /dev/null +++ b/core/java/android/hardware/location/NanoAppRpcService.java @@ -0,0 +1,159 @@ +/* + * Copyright 2021 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 android.hardware.location; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * An RPC service published by a nanoapp. + * + * This class is meant to be informational and allows a nanoapp client to know if a given + * nanoapp publishes a service which supports an RPC interface. The implementation of the RPC + * interface is not specified by the Android APIs and is built by the platform implementation + * over the message payloads transferred through the {@link ContextHubClient}. + * + * This class is instantiated as a part of {@link NanoAppState}, which is provided as a result + * of {@link ContextHubManager.queryNanoApps(ContextHubInfo)}. + * + * See the chrePublishRpcServices() API for how this service is published by the nanoapp. + * + * @hide + */ +@SystemApi +public final class NanoAppRpcService implements Parcelable { + private long mServiceId; + + private int mServiceVersion; + + /** + * @param serviceId The unique ID of this service, see {#getId()}. + * @param serviceVersion The software version of this service, see {#getVersion()}. + */ + public NanoAppRpcService(long serviceId, int serviceVersion) { + mServiceId = serviceId; + mServiceVersion = serviceVersion; + } + + /** + * The unique 64-bit ID of an RPC service published by a nanoapp. Note that + * the uniqueness is only required within the nanoapp's domain (i.e. the + * combination of the nanoapp ID and service id must be unique). + * + * This ID must remain the same for the given nanoapp RPC service once + * published on Android (i.e. must never change). + * + * @return The service ID. + */ + public long getId() { + return mServiceId; + } + + /** + * The software version of this service, which follows the sematic + * versioning scheme (see semver.org). It follows the format + * major.minor.patch, where major and minor versions take up one byte + * each, and the patch version takes up the final 2 (lower) bytes. + * I.e. the version is encoded as 0xMMmmpppp, where MM, mm, pppp are + * the major, minor, patch versions, respectively. + * + * @return The service version. + */ + public int getVersion() { + return mServiceVersion; + } + + /** + * @return The service's major version. + */ + private int getMajorVersion() { + return (mServiceVersion & 0xFF000000) >>> 24; + } + + /** + * @return The service's minor version. + */ + private int getMinorVersion() { + return (mServiceVersion & 0x00FF0000) >>> 16; + } + + /** + * @return The service's patch version. + */ + private int getPatchVersion() { + return mServiceVersion & 0x0000FFFF; + } + + private NanoAppRpcService(Parcel in) { + mServiceId = in.readLong(); + mServiceVersion = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mServiceId); + out.writeInt(mServiceVersion); + } + + public static final @NonNull Creator CREATOR = + new Creator() { + @Override + public NanoAppRpcService createFromParcel(Parcel in) { + return new NanoAppRpcService(in); + } + + @Override + public NanoAppRpcService[] newArray(int size) { + return new NanoAppRpcService[size]; + } + }; + + @NonNull + @Override + public String toString() { + return "NanoAppRpcService[Id = " + Long.toHexString(mServiceId) + ", version = v" + + getMajorVersion() + "." + getMinorVersion() + "." + getPatchVersion() + "]"; + } + + @Override + public boolean equals(@Nullable Object object) { + if (object == this) { + return true; + } + + boolean isEqual = false; + if (object instanceof NanoAppRpcService) { + NanoAppRpcService other = (NanoAppRpcService) object; + isEqual = (other.getId() == mServiceId) + && (other.getVersion() == mServiceVersion); + } + + return isEqual; + } + + @Override + public int hashCode() { + return (int) getId(); + } +} diff --git a/core/java/android/hardware/location/NanoAppState.java b/core/java/android/hardware/location/NanoAppState.java index 96b1f19a9cd57..16d3cd2264cea 100644 --- a/core/java/android/hardware/location/NanoAppState.java +++ b/core/java/android/hardware/location/NanoAppState.java @@ -21,10 +21,16 @@ import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; /** - * A class describing the nanoapp state information resulting from a query to a Context Hub. + * A class describing the nanoapp state information resulting from a query to a Context Hub + * through {@link ContextHubManager.queryNanoApps(ContextHubInfo)}. It contains metadata about + * the nanoapp running on a Context Hub. + * + * See "struct chreNanoappInfo" in the CHRE API (system/chre/chre_api) for additional details. * * @hide */ @@ -33,31 +39,70 @@ public final class NanoAppState implements Parcelable { private long mNanoAppId; private int mNanoAppVersion; private boolean mIsEnabled; - private List mNanoAppPermissions; + private List mNanoAppPermissions = new ArrayList(); + private List mNanoAppRpcServiceList = + new ArrayList(); + /** + * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}. + * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}. + * @param enabled True if the nanoapp is enabled and running on the Context Hub. + */ public NanoAppState(long nanoAppId, int appVersion, boolean enabled) { mNanoAppId = nanoAppId; mNanoAppVersion = appVersion; mIsEnabled = enabled; - mNanoAppPermissions = new ArrayList(); } + /** + * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}. + * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}. + * @param enabled True if the nanoapp is enabled and running on the Context Hub. + * @param nanoAppPersmissions The list of permissions required to communicate with this + * nanoapp. + */ public NanoAppState(long nanoAppId, int appVersion, boolean enabled, @NonNull List nanoAppPermissions) { mNanoAppId = nanoAppId; mNanoAppVersion = appVersion; mIsEnabled = enabled; - mNanoAppPermissions = nanoAppPermissions; + mNanoAppPermissions = Collections.unmodifiableList(nanoAppPermissions); } /** - * @return the NanoAppInfo for this app + * @param nanoAppId The unique ID of this nanoapp, see {#getNanoAppId()}. + * @param appVersion The software version of this nanoapp, see {#getNanoAppVersion()}. + * @param enabled True if the nanoapp is enabled and running on the Context Hub. + * @param nanoAppPersmissions The list of permissions required to communicate with this + * nanoapp. + * @param nanoAppRpcServiceList The list of RPC services published by this nanoapp, see + * {@link NanoAppRpcService} for additional details. + */ + public NanoAppState(long nanoAppId, int appVersion, boolean enabled, + @NonNull List nanoAppPermissions, + @NonNull List nanoAppRpcServiceList) { + mNanoAppId = nanoAppId; + mNanoAppVersion = appVersion; + mIsEnabled = enabled; + mNanoAppPermissions = Collections.unmodifiableList(nanoAppPermissions); + mNanoAppRpcServiceList = Collections.unmodifiableList(nanoAppRpcServiceList); + } + + /** + * @return the unique ID of this nanoapp, which must never change once released on Android. */ public long getNanoAppId() { return mNanoAppId; } /** + * The software version of this service, which follows the sematic + * versioning scheme (see semver.org). It follows the format + * major.minor.patch, where major and minor versions take up one byte + * each, and the patch version takes up the final 2 (lower) bytes. + * I.e. the version is encoded as 0xMMmmpppp, where MM, mm, pppp are + * the major, minor, patch versions, respectively. + * * @return the app version */ public long getNanoAppVersion() { @@ -72,18 +117,29 @@ public final class NanoAppState implements Parcelable { } /** - * @return List of Android permissions that are required to communicate with this app. + * @return A read-only list of Android permissions that are all required to communicate with + * this nanoapp. */ public @NonNull List getNanoAppPermissions() { return mNanoAppPermissions; } + /** + * @return A read-only list of RPC services supported by this nanoapp. + */ + public @NonNull List getRpcServices() { + return mNanoAppRpcServiceList; + } + private NanoAppState(Parcel in) { mNanoAppId = in.readLong(); mNanoAppVersion = in.readInt(); mIsEnabled = (in.readInt() == 1); mNanoAppPermissions = new ArrayList(); in.readStringList(mNanoAppPermissions); + mNanoAppRpcServiceList = Collections.unmodifiableList( + Arrays.asList(in.readParcelableArray( + NanoAppRpcService.class.getClassLoader(), NanoAppRpcService.class))); } @Override @@ -97,6 +153,7 @@ public final class NanoAppState implements Parcelable { out.writeInt(mNanoAppVersion); out.writeInt(mIsEnabled ? 1 : 0); out.writeStringList(mNanoAppPermissions); + out.writeParcelableArray(mNanoAppRpcServiceList.toArray(new NanoAppRpcService[0]), 0); } public static final @android.annotation.NonNull Creator CREATOR = diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java index 2cdf75d98659a..8fdde2486401f 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java @@ -29,6 +29,7 @@ import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubTransaction; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppMessage; +import android.hardware.location.NanoAppRpcService; import android.hardware.location.NanoAppState; import android.util.Log; @@ -211,9 +212,14 @@ import java.util.List; android.hardware.contexthub.NanoappInfo[] nanoAppInfoList) { ArrayList nanoAppStateList = new ArrayList<>(); for (android.hardware.contexthub.NanoappInfo appInfo : nanoAppInfoList) { + ArrayList rpcServiceList = new ArrayList<>(); + for (android.hardware.contexthub.NanoappRpcService service : appInfo.rpcServices) { + rpcServiceList.add(new NanoAppRpcService(service.id, service.version)); + } nanoAppStateList.add( new NanoAppState(appInfo.nanoappId, appInfo.nanoappVersion, - appInfo.enabled, new ArrayList<>(Arrays.asList(appInfo.permissions)))); + appInfo.enabled, new ArrayList<>(Arrays.asList(appInfo.permissions)), + rpcServiceList)); } return nanoAppStateList;