1 /*
2  * Copyright (C) 2024 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.serviceflow;
18 
19 import static com.android.ondevicepersonalization.services.PhFlags.KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED;
20 
21 import android.os.Bundle;
22 
23 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
24 import com.android.ondevicepersonalization.services.FlagsFactory;
25 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
26 import com.android.ondevicepersonalization.services.process.IsolatedServiceInfo;
27 import com.android.ondevicepersonalization.services.process.PluginProcessRunner;
28 import com.android.ondevicepersonalization.services.process.ProcessRunner;
29 import com.android.ondevicepersonalization.services.process.SharedIsolatedProcessRunner;
30 
31 import com.google.common.util.concurrent.FluentFuture;
32 import com.google.common.util.concurrent.Futures;
33 import com.google.common.util.concurrent.ListenableFuture;
34 import com.google.common.util.concurrent.ListeningExecutorService;
35 
36 /**
37  * Task object representing a service flow task.
38  */
39 public class ServiceFlowTask {
40 
41     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
42     private static final String TAG = ServiceFlowTask.class.getSimpleName();
43 
44     private final ServiceFlowType mServiceFlowType;
45     private final ServiceFlow mServiceFlow;
46     private final ProcessRunner mProcessRunner;
47     private volatile boolean mIsCompleted;
48     private volatile Exception mExecutionException;
49 
50     private final ListeningExecutorService mExecutor =
51             OnDevicePersonalizationExecutors.getBackgroundExecutor();
52 
ServiceFlowTask(ServiceFlowType serviceFlowType, ServiceFlow serviceFlow)53     public ServiceFlowTask(ServiceFlowType serviceFlowType, ServiceFlow serviceFlow) {
54         mIsCompleted = false;
55         mServiceFlowType = serviceFlowType;
56         mServiceFlow = serviceFlow;
57         mProcessRunner =
58                 (boolean) FlagsFactory.getFlags()
59                         .getStableFlag(KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED)
60                         ? SharedIsolatedProcessRunner.getInstance()
61                         : PluginProcessRunner.getInstance();
62     }
63 
getServiceFlowType()64     public ServiceFlowType getServiceFlowType() {
65         return mServiceFlowType;
66     }
67 
getServiceFlow()68     public ServiceFlow getServiceFlow() {
69         return mServiceFlow;
70     }
71 
isCompleted()72     public boolean isCompleted() {
73         return mIsCompleted;
74     }
75 
getExecutionException()76     public Exception getExecutionException() {
77         return mExecutionException;
78     }
79 
80     /** Executes the given service flow. */
run()81     public void run() {
82         try {
83             if (mIsCompleted || !mServiceFlow.isServiceFlowReady()) {
84                 sLogger.d(TAG + ": Unexpected service flow state for " + mServiceFlowType);
85                 return;
86             }
87 
88             ListenableFuture<IsolatedServiceInfo> loadServiceFuture =
89                     mProcessRunner.loadIsolatedService(
90                             mServiceFlowType.getTaskName(), mServiceFlow.getService());
91 
92             ListenableFuture<Bundle> runServiceFuture = FluentFuture.from(loadServiceFuture)
93                     .transformAsync(
94                             isolatedServiceInfo -> mProcessRunner
95                                     .runIsolatedService(
96                                             isolatedServiceInfo,
97                                             mServiceFlowType.getOperationCode(),
98                                             mServiceFlow.getServiceParams()),
99                             mExecutor);
100 
101             mServiceFlow.uploadServiceFlowMetrics(runServiceFuture);
102 
103             ListenableFuture<?> serviceFlowResultFuture =
104                     mServiceFlow.getServiceFlowResultFuture(runServiceFuture);
105 
106             mServiceFlow.returnResultThroughCallback(serviceFlowResultFuture);
107 
108             var unused =
109                     Futures.whenAllComplete(loadServiceFuture, serviceFlowResultFuture)
110                             .callAsync(
111                                     () -> {
112                                         mServiceFlow.cleanUpServiceParams();
113                                         ListenableFuture<Void> unloadServiceFuture =
114                                                 mProcessRunner.unloadIsolatedService(
115                                                         loadServiceFuture.get());
116                                         mIsCompleted = true;
117                                         return unloadServiceFuture;
118                                     }, mExecutor);
119         } catch (Exception e) {
120             sLogger.w(TAG + ": ServiceFlowTask " + mServiceFlowType + "failed. " + e);
121             mExecutionException = e;
122         }
123     }
124 }
125