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.impl; 18 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.os.RemoteException; 22 import android.util.Log; 23 24 import com.android.ondevicepersonalization.libraries.plugin.FailureType; 25 import com.android.ondevicepersonalization.libraries.plugin.PluginApplication; 26 import com.android.ondevicepersonalization.libraries.plugin.PluginCallback; 27 import com.android.ondevicepersonalization.libraries.plugin.PluginController; 28 import com.android.ondevicepersonalization.libraries.plugin.PluginHost; 29 import com.android.ondevicepersonalization.libraries.plugin.PluginInfo; 30 import com.android.ondevicepersonalization.libraries.plugin.PluginState; 31 import com.android.ondevicepersonalization.libraries.plugin.PluginStateCallback; 32 import com.android.ondevicepersonalization.libraries.plugin.internal.CallbackConverter; 33 import com.android.ondevicepersonalization.libraries.plugin.internal.IPluginCallback; 34 import com.android.ondevicepersonalization.libraries.plugin.internal.IPluginExecutorService; 35 import com.android.ondevicepersonalization.libraries.plugin.internal.IPluginStateCallback; 36 import com.android.ondevicepersonalization.libraries.plugin.internal.PluginArchiveManager; 37 import com.android.ondevicepersonalization.libraries.plugin.internal.PluginExecutorServiceProvider; 38 import com.android.ondevicepersonalization.libraries.plugin.internal.PluginInfoInternal; 39 40 import com.google.common.util.concurrent.SettableFuture; 41 42 import org.checkerframework.checker.nullness.qual.Nullable; 43 44 /** 45 * Implementation of {@link PluginController} that executes {@link Plugin} implementations in the 46 * {@link IPluginExecutorService} provided by the passed in {@link PluginExecutorServiceProvider}. 47 */ 48 public class PluginControllerImpl implements PluginController { 49 private static final String TAG = PluginControllerImpl.class.getSimpleName(); 50 private final PluginInfo mInfo; 51 private final Context mContext; 52 private final PluginExecutorServiceProvider mPluginExecutorServiceProvider; 53 private final PluginArchiveManager mPluginArchiveManager; 54 PluginControllerImpl( Context context, PluginExecutorServiceProvider pluginExecutorServiceProvider, PluginInfo info)55 public PluginControllerImpl( 56 Context context, 57 PluginExecutorServiceProvider pluginExecutorServiceProvider, 58 PluginInfo info) { 59 this.mContext = context; 60 this.mInfo = info; 61 this.mPluginExecutorServiceProvider = pluginExecutorServiceProvider; 62 this.mPluginArchiveManager = new PluginArchiveManager(context); 63 } 64 loadInternal(PluginInfoInternal infoInternal, PluginCallback callback)65 private void loadInternal(PluginInfoInternal infoInternal, PluginCallback callback) 66 throws RemoteException { 67 Context applicationContext = mContext.getApplicationContext(); 68 @Nullable PluginApplication pluginApplication = null; 69 @Nullable PluginHost pluginHost = null; 70 if (applicationContext instanceof PluginApplication) { 71 pluginApplication = (PluginApplication) applicationContext; 72 pluginHost = pluginApplication.getPluginHost(); 73 } 74 75 @Nullable Bundle pluginContextInitData = null; 76 if (pluginHost != null) { 77 pluginContextInitData = pluginHost.createPluginContextInitData(mInfo.taskName()); 78 } 79 80 IPluginCallback parcelablePluginCallback = CallbackConverter.toIPluginCallback(callback); 81 82 mPluginExecutorServiceProvider 83 .getExecutorService() 84 .load(infoInternal, parcelablePluginCallback, pluginContextInitData); 85 } 86 87 @Override load(PluginCallback callback)88 public void load(PluginCallback callback) throws RemoteException { 89 if (!mPluginExecutorServiceProvider.bindService()) { 90 Log.e(TAG, "Failed to bind to service"); 91 callback.onFailure(FailureType.ERROR_LOADING_PLUGIN); 92 return; 93 } 94 95 PluginInfoInternal.Builder infoBuilder = PluginInfoInternal.builder(); 96 infoBuilder.setTaskName(mInfo.taskName()); 97 infoBuilder.setEntryPointClassName(mInfo.entryPointClassName()); 98 99 PluginArchiveManager.PluginTask task = infoInternal -> loadInternal(infoInternal, callback); 100 101 SettableFuture<Boolean> serviceReadiness = 102 mPluginExecutorServiceProvider.getExecutorServiceReadiness(); 103 104 if (!mPluginArchiveManager.copyPluginArchivesToCacheAndAwaitService( 105 serviceReadiness, "PluginExecutorService", infoBuilder, mInfo.archives(), task)) { 106 callback.onFailure(FailureType.ERROR_LOADING_PLUGIN); 107 } 108 } 109 110 @Override unload(PluginCallback callback)111 public void unload(PluginCallback callback) throws RemoteException { 112 IPluginExecutorService pluginExecutorService = 113 mPluginExecutorServiceProvider.getExecutorService(); 114 if (pluginExecutorService == null) { 115 callback.onFailure(FailureType.ERROR_UNLOADING_PLUGIN); 116 return; 117 } 118 119 IPluginCallback parcelablePluginCallback = CallbackConverter.toIPluginCallback(callback); 120 try { 121 pluginExecutorService.unload(mInfo.taskName(), parcelablePluginCallback); 122 mPluginExecutorServiceProvider.unbindService(); 123 } catch (RemoteException e) { 124 // This callback call may throw RemoteException, which we pass on. 125 callback.onFailure(FailureType.ERROR_UNLOADING_PLUGIN); 126 } 127 } 128 129 @Override execute(Bundle input, PluginCallback callback)130 public void execute(Bundle input, PluginCallback callback) throws RemoteException { 131 IPluginExecutorService pluginExecutorService = 132 mPluginExecutorServiceProvider.getExecutorService(); 133 if (pluginExecutorService == null) { 134 callback.onFailure(FailureType.ERROR_EXECUTING_PLUGIN); 135 return; 136 } 137 138 IPluginCallback parcelablePluginCallback = CallbackConverter.toIPluginCallback(callback); 139 try { 140 pluginExecutorService.execute(mInfo.taskName(), input, parcelablePluginCallback); 141 } catch (RemoteException e) { 142 // This callback call may throw RemoteException, which we pass on. 143 callback.onFailure(FailureType.ERROR_EXECUTING_PLUGIN); 144 } 145 } 146 147 @Override checkPluginState(PluginStateCallback callback)148 public void checkPluginState(PluginStateCallback callback) { 149 IPluginExecutorService pluginExecutorService = 150 mPluginExecutorServiceProvider.getExecutorService(); 151 if (pluginExecutorService == null) { 152 callback.onState(PluginState.STATE_NO_SERVICE); 153 return; 154 } 155 IPluginStateCallback parcelableStateCallback = 156 CallbackConverter.toIPluginStateCallback(callback); 157 try { 158 pluginExecutorService.checkPluginState(mInfo.taskName(), parcelableStateCallback); 159 } catch (RemoteException e) { 160 callback.onState(PluginState.STATE_EXCEPTION_THROWN); 161 } 162 } 163 164 @Override getName()165 public String getName() { 166 return mInfo.taskName(); 167 } 168 } 169