• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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