diff --git a/core/java/android/rag/IToolRegistry.aidl b/core/java/android/rag/IToolRegistry.aidl new file mode 100644 index 0000000000000..33699801b9ef8 --- /dev/null +++ b/core/java/android/rag/IToolRegistry.aidl @@ -0,0 +1,42 @@ +package android.rag; + +/** + * Public Binder interface for the JarvisOS Tool Registry. + * + * Published as service name "jarvis_tools" from RagService. + * Apps use this to inspect what tools are registered on the device. + * + * Return value contract: + * - listTools() / searchTools() return "[]" when store is not ready or no results. + * - getTool(id) returns null when not found or store is not ready. + * - All other failures return "[]" (list methods) or null (single-item methods). + * + * Note: listTools() returns the full registry in one Binder call. This is fine + * for small registries but risks TransactionTooLargeException at scale. + * Pagination (offset/limit) will be added in a future phase. + */ +interface IToolRegistry { + + /** + * Returns a JSON array of all registered tools, or "[]" if none / not ready. + * Each element is a tool object: + * {id, toolName, description, paramsJson, rawDefinition, receiverClass, + * cactusIndexId, app:{id, packageName, appLabel, sourceType}} + * + * Note: paramsJson is itself a JSON-encoded string, not an embedded JSON object. + */ + String listTools(); + + /** + * Returns the JSON object for a single tool by its ObjectBox id. + * Returns null if not found. + */ + String getTool(long id); + + /** + * Semantic + metadata search over registered tools. + * Returns a JSON array of matching tools (up to TOP_K), ordered by relevance. + * Uses Cactus HNSW if available, falls back to keyword match. + */ + String searchTools(String query); +} diff --git a/services/core/java/com/android/server/rag/Android.bp b/services/core/java/com/android/server/rag/Android.bp index e9e61fb843e25..9e8ea53c29bed 100644 --- a/services/core/java/com/android/server/rag/Android.bp +++ b/services/core/java/com/android/server/rag/Android.bp @@ -1,4 +1,10 @@ // Build file for RAG System Service +// +// ObjectBox note: AppRecord_, ToolRecord_, SourceFile_, DocumentChunk_, etc. +// are generated by the ObjectBox annotation processor at build time. +// To generate: add objectbox-processor as a java_plugin once the processor +// JAR is available in vendor/jarvisos/prebuilts/objectbox/. +// Ref: https://docs.objectbox.io/android/android-build-system java_library { name: "services.rag", srcs: [ diff --git a/services/core/java/com/android/server/rag/RagService.java b/services/core/java/com/android/server/rag/RagService.java index 41e667012111c..f7d862f8055a0 100644 --- a/services/core/java/com/android/server/rag/RagService.java +++ b/services/core/java/com/android/server/rag/RagService.java @@ -4,6 +4,7 @@ import android.os.Binder; import android.os.Environment; import android.app.rag.IRagService; +import android.rag.IToolRegistry; import android.util.Log; import com.android.server.SystemService; @@ -14,6 +15,7 @@ import com.android.server.rag.indexing.RagIndexWorker; import com.android.server.rag.model.SourceFile; import com.android.server.rag.tools.ToolDispatcher; +import com.android.server.rag.tools.ToolRecord; import com.android.server.rag.tools.ToolScannerService; /** @@ -59,6 +61,7 @@ public RagService(Context context) { @Override public void onStart() { publishBinderService("rag", mBinder); + publishBinderService("jarvis_tools", mToolRegistryBinder); initializeAsync(); } @@ -204,4 +207,56 @@ private void enforceCallingPermission() { // TODO: mContext.enforceCallingPermission("android.permission.ACCESS_RAG_SERVICE", "..."); } }; + + // ------------------------------------------------------------------------- + // IToolRegistry Binder — published as "jarvis_tools" + // ------------------------------------------------------------------------- + + private final IToolRegistry.Stub mToolRegistryBinder = new IToolRegistry.Stub() { + + @Override + public String listTools() { + enforceCallingPermission(); + if (!JarvisStore.isReady()) return "[]"; + try { + java.util.List all = JarvisStore.box(ToolRecord.class).getAll(); + return ToolDispatcher.serializeTools(all); + } catch (Exception e) { + Log.e(TAG, "listTools() failed", e); + return "[]"; + } + } + + @Override + public String getTool(long id) { + enforceCallingPermission(); + if (!JarvisStore.isReady()) return null; + try { + ToolRecord tool = JarvisStore.box(ToolRecord.class).get(id); + return ToolDispatcher.serializeTool(tool); + } catch (Exception e) { + Log.e(TAG, "getTool() failed for id=" + id, e); + return null; + } + } + + @Override + public String searchTools(String query) { + enforceCallingPermission(); + if (query == null || query.trim().isEmpty()) return "[]"; + if (!mIsReady || mToolDispatcher == null) return "[]"; + try { + return mToolDispatcher.searchTools(query); + } catch (Exception e) { + Log.e(TAG, "searchTools() failed", e); + return "[]"; + } + } + + private void enforceCallingPermission() { + final int callingUid = Binder.getCallingUid(); + Log.d(TAG, "jarvis_tools called by UID: " + callingUid); + // TODO: mContext.enforceCallingPermission("android.permission.ACCESS_RAG_SERVICE", "..."); + } + }; } diff --git a/services/core/java/com/android/server/rag/tools/ToolDefinition.java b/services/core/java/com/android/server/rag/tools/ToolDefinition.java deleted file mode 100644 index cee30c564dea3..0000000000000 --- a/services/core/java/com/android/server/rag/tools/ToolDefinition.java +++ /dev/null @@ -1,2 +0,0 @@ -// DELETED — replaced by AppRecord.java + ToolRecord.java -// See TOOL_REGISTRY.md and commit history. diff --git a/services/core/java/com/android/server/rag/tools/ToolDispatcher.java b/services/core/java/com/android/server/rag/tools/ToolDispatcher.java index e4ca1d89e8c77..1d4a9896e0499 100644 --- a/services/core/java/com/android/server/rag/tools/ToolDispatcher.java +++ b/services/core/java/com/android/server/rag/tools/ToolDispatcher.java @@ -343,6 +343,68 @@ protected void onReceiveResult(int resultCode, Bundle resultData) { return result; } + // ------------------------------------------------------------------------- + // Public search API (used by IToolRegistry) + // ------------------------------------------------------------------------- + + /** + * Search for tools matching the query without dispatching. + * Returns a JSON array string (never null; returns "[]" on empty/failure). + */ + public String searchTools(String query) { + if (query == null || query.trim().isEmpty()) return "[]"; + List results = semanticSearch(query); + return serializeTools(results); + } + + /** + * Serialize a single ToolRecord to a JSONObject. + * Returns null if tool is null or serialization fails. + */ + public static JSONObject serializeToolObject(ToolRecord tool) { + if (tool == null) return null; + try { + JSONObject obj = new JSONObject(); + obj.put("id", tool.id); + obj.put("toolName", tool.toolName != null ? tool.toolName : ""); + obj.put("description", tool.description != null ? tool.description : ""); + obj.put("paramsJson", tool.paramsJson != null ? tool.paramsJson : ""); + obj.put("rawDefinition", tool.rawDefinition != null ? tool.rawDefinition : ""); + obj.put("receiverClass", tool.receiverClass != null ? tool.receiverClass : ""); + obj.put("cactusIndexId", tool.cactusIndexId); + + AppRecord app = tool.app.getTarget(); + if (app != null) { + JSONObject appObj = new JSONObject(); + appObj.put("id", app.id); + appObj.put("packageName", app.packageName != null ? app.packageName : ""); + appObj.put("appLabel", app.appLabel != null ? app.appLabel : ""); + appObj.put("sourceType", app.sourceType != null ? app.sourceType : ""); + obj.put("app", appObj); + } + return obj; + } catch (JSONException e) { + Log.e(TAG, "serializeToolObject failed for: " + tool.toolName, e); + return null; + } + } + + /** Serialize a single ToolRecord to a JSON string, or null on failure. */ + public static String serializeTool(ToolRecord tool) { + JSONObject obj = serializeToolObject(tool); + return obj != null ? obj.toString() : null; + } + + /** Serialize a list of ToolRecords to a JSON array string. */ + public static String serializeTools(List tools) { + JSONArray arr = new JSONArray(); + for (ToolRecord t : tools) { + JSONObject obj = serializeToolObject(t); + if (obj != null) arr.put(obj); + } + return arr.toString(); + } + // ------------------------------------------------------------------------- // Internal data class // -------------------------------------------------------------------------