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.services.process; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.ondevicepersonalization.Constants; 23 import android.ondevicepersonalization.OnDevicePersonalizationException; 24 import android.os.Bundle; 25 import android.util.Log; 26 27 import androidx.concurrent.futures.CallbackToFutureAdapter; 28 29 import com.android.ondevicepersonalization.libraries.plugin.FailureType; 30 import com.android.ondevicepersonalization.libraries.plugin.PluginCallback; 31 import com.android.ondevicepersonalization.libraries.plugin.PluginController; 32 import com.android.ondevicepersonalization.libraries.plugin.PluginInfo; 33 import com.android.ondevicepersonalization.libraries.plugin.PluginManager; 34 import com.android.ondevicepersonalization.libraries.plugin.impl.PluginManagerImpl; 35 36 import com.google.common.collect.ImmutableList; 37 import com.google.common.util.concurrent.Futures; 38 import com.google.common.util.concurrent.ListenableFuture; 39 40 import java.util.Objects; 41 42 /** Utilities to support loading and executing plugins. */ 43 public class ProcessUtils { 44 private static final String TAG = "ProcessUtils"; 45 private static final String ENTRY_POINT_CLASS = 46 "com.android.ondevicepersonalization.services.process.OnDevicePersonalizationPlugin"; 47 48 public static final String PARAM_CLASS_NAME_KEY = "param.classname"; 49 public static final String PARAM_OPERATION_KEY = "param.operation"; 50 public static final String PARAM_SERVICE_INPUT = "param.service_input"; 51 52 private static PluginManager sPluginManager; 53 54 /** Loads a service in an isolated process */ loadIsolatedService( @onNull String taskName, @NonNull String packageName, @NonNull Context context)55 @NonNull public static ListenableFuture<IsolatedServiceInfo> loadIsolatedService( 56 @NonNull String taskName, @NonNull String packageName, 57 @NonNull Context context) { 58 try { 59 Log.d(TAG, "loadIsolatedService: " + packageName); 60 return loadPlugin(createPluginController( 61 createPluginId(packageName, taskName), 62 getPluginManager(context), packageName)); 63 } catch (Exception e) { 64 return Futures.immediateFailedFuture(e); 65 } 66 } 67 68 /** Executes a service loaded in an isolated process */ runIsolatedService( @onNull IsolatedServiceInfo isolatedProcessInfo, @NonNull String className, int operationCode, @NonNull Bundle serviceParams)69 @NonNull public static ListenableFuture<Bundle> runIsolatedService( 70 @NonNull IsolatedServiceInfo isolatedProcessInfo, 71 @NonNull String className, 72 int operationCode, 73 @NonNull Bundle serviceParams) { 74 Log.d(TAG, "runIsolatedService: " + className + " op: " + operationCode); 75 Bundle pluginParams = new Bundle(); 76 pluginParams.putString(PARAM_CLASS_NAME_KEY, className); 77 pluginParams.putInt(PARAM_OPERATION_KEY, operationCode); 78 pluginParams.putParcelable(PARAM_SERVICE_INPUT, serviceParams); 79 return executePlugin(isolatedProcessInfo.getPluginController(), pluginParams); 80 } 81 getPluginManager(@onNull Context context)82 @NonNull static PluginManager getPluginManager(@NonNull Context context) { 83 synchronized (ProcessUtils.class) { 84 if (sPluginManager == null) { 85 sPluginManager = new PluginManagerImpl(context); 86 } 87 return sPluginManager; 88 } 89 } 90 createPluginController( String taskName, @NonNull PluginManager pluginManager, @Nullable String apkName)91 @NonNull static PluginController createPluginController( 92 String taskName, @NonNull PluginManager pluginManager, @Nullable String apkName) 93 throws Exception { 94 PluginInfo info = PluginInfo.createJvmInfo( 95 taskName, getArchiveList(apkName), ENTRY_POINT_CLASS); 96 return Objects.requireNonNull(pluginManager.createPluginController(info)); 97 } 98 loadPlugin( @onNull PluginController pluginController)99 @NonNull static ListenableFuture<IsolatedServiceInfo> loadPlugin( 100 @NonNull PluginController pluginController) { 101 return CallbackToFutureAdapter.getFuture( 102 completer -> { 103 try { 104 Log.d(TAG, "loadPlugin"); 105 pluginController.load(new PluginCallback() { 106 @Override public void onSuccess(Bundle bundle) { 107 completer.set(new IsolatedServiceInfo(pluginController)); 108 } 109 @Override public void onFailure(FailureType failure) { 110 completer.setException(new OnDevicePersonalizationException( 111 Constants.STATUS_INTERNAL_ERROR, 112 String.format("loadPlugin failed. %s", failure.toString()))); 113 } 114 }); 115 } catch (Exception e) { 116 completer.setException(e); 117 } 118 return "loadPlugin"; 119 } 120 ); 121 } 122 123 @NonNull static ListenableFuture<Bundle> executePlugin( 124 @NonNull PluginController pluginController, @NonNull Bundle pluginParams) { 125 return CallbackToFutureAdapter.getFuture( 126 completer -> { 127 try { 128 Log.d(TAG, "executePlugin"); 129 pluginController.execute(pluginParams, new PluginCallback() { 130 @Override public void onSuccess(Bundle bundle) { 131 completer.set(bundle); 132 } 133 @Override public void onFailure(FailureType failure) { 134 completer.setException(new OnDevicePersonalizationException( 135 Constants.STATUS_INTERNAL_ERROR, 136 String.format("executePlugin failed: %s", failure.toString()))); 137 } 138 }); 139 } catch (Exception e) { 140 completer.setException(e); 141 } 142 return "executePlugin"; 143 } 144 ); 145 } 146 147 @NonNull static ImmutableList<PluginInfo.ArchiveInfo> getArchiveList( 148 @Nullable String apkName) { 149 if (apkName == null) { 150 return ImmutableList.of(); 151 } 152 ImmutableList.Builder<PluginInfo.ArchiveInfo> archiveInfoBuilder = ImmutableList.builder(); 153 archiveInfoBuilder.add( 154 PluginInfo.ArchiveInfo.builder().setPackageName(apkName).build()); 155 return archiveInfoBuilder.build(); 156 } 157 158 static String createPluginId(String vendorPackageName, String taskName) { 159 // TODO(b/249345663) Perform any validation needed on the input. 160 return vendorPackageName + "-" + taskName; 161 } 162 163 private ProcessUtils() {} 164 } 165