1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.scriptexecutor; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.os.Binder; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.os.IBinder; 25 import android.os.ParcelFileDescriptor; 26 import android.os.PersistableBundle; 27 import android.os.Process; 28 import android.os.RemoteException; 29 import android.os.UserHandle; 30 import android.util.Log; 31 32 import com.android.car.internal.LargeParcelable; 33 import com.android.car.telemetry.scriptexecutorinterface.BundleList; 34 import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutor; 35 import com.android.car.telemetry.scriptexecutorinterface.IScriptExecutorListener; 36 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.util.List; 40 41 /** 42 * Executes Lua code in an isolated process with provided source code 43 * and input arguments. 44 */ 45 public final class ScriptExecutor extends Service { 46 static { 47 System.loadLibrary("scriptexecutorjni"); 48 } 49 50 private static final String TAG = ScriptExecutor.class.getSimpleName(); 51 52 // Dedicated "worker" thread to handle all calls related to native code. 53 private HandlerThread mNativeHandlerThread; 54 // Handler associated with the native worker thread. 55 private Handler mNativeHandler; 56 57 private final class IScriptExecutorImpl extends IScriptExecutor.Stub { 58 @Override invokeScript(String scriptBody, String functionName, PersistableBundle publishedData, PersistableBundle savedState, IScriptExecutorListener listener)59 public void invokeScript(String scriptBody, String functionName, 60 PersistableBundle publishedData, PersistableBundle savedState, 61 IScriptExecutorListener listener) throws SecurityException { 62 ensureCallerIsSystem(); 63 mNativeHandler.post(() -> 64 nativeInvokeScript(mLuaEnginePtr, scriptBody, functionName, publishedData, 65 null, savedState, listener)); 66 } 67 68 @Override invokeScriptForLargeInput(String scriptBody, String functionName, ParcelFileDescriptor publishedDataFileDescriptor, PersistableBundle savedState, IScriptExecutorListener listener)69 public void invokeScriptForLargeInput(String scriptBody, String functionName, 70 ParcelFileDescriptor publishedDataFileDescriptor, PersistableBundle savedState, 71 IScriptExecutorListener listener) throws SecurityException { 72 ensureCallerIsSystem(); 73 mNativeHandler.post(() -> { 74 PersistableBundle publishedData; 75 try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream( 76 publishedDataFileDescriptor)) { 77 publishedData = PersistableBundle.readFromStream(input); 78 } catch (IOException e) { 79 try { 80 listener.onError(IScriptExecutorListener.ERROR_TYPE_SCRIPT_EXECUTOR_ERROR, 81 e.getMessage(), ""); 82 } catch (RemoteException remoteException) { 83 if (Log.isLoggable(TAG, Log.ERROR)) { 84 // At least log "message" here, in case it was never sent back via 85 // the callback. 86 Log.e(TAG, "failed while calling listener with exception ", e); 87 } 88 } 89 return; 90 } 91 92 nativeInvokeScript(mLuaEnginePtr, scriptBody, functionName, publishedData, 93 null, savedState, listener); 94 }); 95 } 96 97 @Override invokeScriptForBundleList(String scriptBody, String functionName, BundleList bundleList, PersistableBundle savedState, IScriptExecutorListener listener)98 public void invokeScriptForBundleList(String scriptBody, String functionName, 99 BundleList bundleList, PersistableBundle savedState, 100 IScriptExecutorListener listener) throws SecurityException { 101 ensureCallerIsSystem(); 102 BundleList reconstBundles = 103 (BundleList) LargeParcelable.reconstructStableAIDLParcelable( 104 bundleList, /*keepSharedMemory=*/false); 105 mNativeHandler.post(() -> 106 nativeInvokeScript(mLuaEnginePtr, scriptBody, functionName, null, 107 reconstBundles.bundles, savedState, listener)); 108 } 109 } 110 111 private IScriptExecutorImpl mScriptExecutorBinder; 112 113 // Memory location of Lua Engine object which is allocated in native code. 114 private long mLuaEnginePtr; 115 116 @Override onCreate()117 public void onCreate() { 118 super.onCreate(); 119 120 mNativeHandlerThread = new HandlerThread(ScriptExecutor.class.getSimpleName()); 121 mNativeHandlerThread.start(); 122 mNativeHandler = new Handler(mNativeHandlerThread.getLooper()); 123 124 mLuaEnginePtr = nativeInitLuaEngine(); 125 mScriptExecutorBinder = new IScriptExecutorImpl(); 126 } 127 128 @Override onDestroy()129 public void onDestroy() { 130 super.onDestroy(); 131 nativeDestroyLuaEngine(mLuaEnginePtr); 132 mNativeHandlerThread.quit(); 133 } 134 135 @Override onBind(Intent intent)136 public IBinder onBind(Intent intent) { 137 return mScriptExecutorBinder; 138 } 139 ensureCallerIsSystem()140 private void ensureCallerIsSystem() throws SecurityException { 141 if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) { 142 throw new SecurityException("ScriptExecutor called from non-system user"); 143 } 144 } 145 146 /** 147 * Initializes Lua Engine. 148 * 149 * <p>Returns memory location of Lua Engine. 150 */ nativeInitLuaEngine()151 private native long nativeInitLuaEngine(); 152 153 /** 154 * Destroys LuaEngine at the provided memory address. 155 */ nativeDestroyLuaEngine(long luaEnginePtr)156 private native void nativeDestroyLuaEngine(long luaEnginePtr); 157 158 /** 159 * Calls provided Lua function. 160 * 161 * @param luaEnginePtr memory address of the stored LuaEngine instance. 162 * @param scriptBody complete body of Lua script that also contains the function to be 163 * invoked. 164 * @param functionName the name of the function to execute. 165 * @param publishedData input data provided by the source which the function handles. 166 * @param bundleList input data list provided by the source which the function handles. 167 * @param savedState key-value pairs preserved from the previous invocation of the function. 168 * @param listener callback for the sandboxed environment to report back script execution 169 * results 170 * and errors. 171 */ nativeInvokeScript(long luaEnginePtr, String scriptBody, String functionName, PersistableBundle publishedData, List<PersistableBundle> bundleList, PersistableBundle savedState, IScriptExecutorListener listener)172 private native void nativeInvokeScript(long luaEnginePtr, String scriptBody, 173 String functionName, PersistableBundle publishedData, 174 List<PersistableBundle> bundleList, PersistableBundle savedState, 175 IScriptExecutorListener listener); 176 } 177