1 /* 2 * Copyright (C) 2022 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.ondevicepersonalization.libraries.plugin.internal; 18 19 import android.app.Service; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import com.android.ondevicepersonalization.libraries.plugin.FailureType; 28 import com.android.ondevicepersonalization.libraries.plugin.Plugin; 29 import com.android.ondevicepersonalization.libraries.plugin.PluginApplication; 30 import com.android.ondevicepersonalization.libraries.plugin.PluginCallback; 31 import com.android.ondevicepersonalization.libraries.plugin.PluginHost; 32 import com.android.ondevicepersonalization.libraries.plugin.PluginState; 33 34 import org.checkerframework.checker.nullness.qual.Nullable; 35 36 /** Service that loads, and executes {@link Plugin} implementations. */ 37 public class PluginExecutorService extends Service { 38 public static final String TAG = "PluginExecutorService"; 39 private PluginExecutor mPluginExecutor; 40 PluginExecutorService()41 public PluginExecutorService() {} 42 43 @Nullable PluginApplication mPluginApplication = null; 44 45 @Override onCreate()46 public void onCreate() { 47 super.onCreate(); 48 Context applicationContext = getApplicationContext(); 49 if (applicationContext == null) { 50 Log.e(TAG, "PluginExecutorService.onCreate() got null application context"); 51 return; 52 } 53 mPluginExecutor = PluginExecutor.create(applicationContext, new PluginLoaderImpl()); 54 55 // Expect the Application context to implement the {@link PluginApplication} interface. The 56 // {@link PluginApplication}'s purpose is to supply an optional {@link PluginHost} 57 // implementation. 58 if (!(applicationContext instanceof PluginApplication)) { 59 Log.e( 60 TAG, 61 String.format( 62 "PluginExecutorService.onCreate() application context not instance of" 63 + " PluginApplication")); 64 return; 65 } 66 mPluginApplication = (PluginApplication) applicationContext; 67 } 68 69 @Override onBind(Intent intent)70 public @Nullable IBinder onBind(Intent intent) { 71 72 return new IPluginExecutorService.Stub() { 73 @Override 74 public void load( 75 PluginInfoInternal info, 76 IPluginCallback pluginCallback, 77 @Nullable Bundle pluginContextInitData) { 78 79 // The {@link PluginHost} provides a method to create a {@link PluginContext}. 80 @Nullable PluginHost pluginHost = null; 81 if (mPluginApplication != null) { 82 pluginHost = mPluginApplication.getPluginHost(); 83 } 84 85 PluginCallback publicPluginCallback = 86 CallbackConverter.toPublicCallback(pluginCallback); 87 try { 88 mPluginExecutor.load( 89 info, publicPluginCallback, pluginHost, pluginContextInitData); 90 } catch (RemoteException e) { 91 try { 92 pluginCallback.onFailure(FailureType.ERROR_LOADING_PLUGIN); 93 } catch (RemoteException e2) { 94 Log.e(TAG, "load() failed to call pluginCallback.onFailure()"); 95 } 96 } 97 } 98 99 @Override 100 public void execute( 101 String pluginName, Bundle input, IPluginCallback pluginCallback) { 102 // TODO(b/231347987): we need extra logic somewhere that can validated the contents 103 // of the 104 // output Bundle. 105 PluginCallback publicPluginCallback = 106 CallbackConverter.toPublicCallback(pluginCallback); 107 try { 108 mPluginExecutor.execute(input, pluginName, publicPluginCallback); 109 } catch (RemoteException e) { 110 try { 111 pluginCallback.onFailure(FailureType.ERROR_EXECUTING_PLUGIN); 112 } catch (RemoteException e2) { 113 Log.e(TAG, "execute() failed to call pluginCallback.onFailure()"); 114 } 115 } 116 } 117 118 @Override 119 public void unload(String pluginName, IPluginCallback pluginCallback) { 120 PluginCallback publicPluginCallback = 121 CallbackConverter.toPublicCallback(pluginCallback); 122 try { 123 mPluginExecutor.unload(pluginName, publicPluginCallback); 124 } catch (RemoteException e) { 125 try { 126 pluginCallback.onFailure(FailureType.ERROR_UNLOADING_PLUGIN); 127 } catch (RemoteException e2) { 128 Log.e(TAG, "unload() failed to call pluginCallback.onFailure()"); 129 } 130 } 131 } 132 133 @Override 134 public void checkPluginState(String pluginName, IPluginStateCallback stateCallback) { 135 try { 136 mPluginExecutor.checkPluginState(pluginName, stateCallback); 137 } catch (RemoteException e) { 138 try { 139 stateCallback.onState(PluginState.STATE_EXCEPTION_THROWN); 140 } catch (RemoteException e2) { 141 Log.e(TAG, "checkPluginState() failed to call stateCallback.onState()"); 142 } 143 } 144 } 145 }; 146 } 147 } 148