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