• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.request;
18 
19 import android.adservices.ondevicepersonalization.CalleeMetadata;
20 import android.adservices.ondevicepersonalization.Constants;
21 import android.adservices.ondevicepersonalization.RenderInputParcel;
22 import android.adservices.ondevicepersonalization.RenderOutputParcel;
23 import android.adservices.ondevicepersonalization.RenderingConfig;
24 import android.adservices.ondevicepersonalization.RequestLogRecord;
25 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback;
26 import android.annotation.NonNull;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.os.Bundle;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.os.SystemClock;
33 import android.view.SurfaceControlViewHost.SurfacePackage;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.odp.module.common.Clock;
37 import com.android.odp.module.common.MonotonicClock;
38 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
39 import com.android.ondevicepersonalization.services.Flags;
40 import com.android.ondevicepersonalization.services.FlagsFactory;
41 import com.android.ondevicepersonalization.services.OdpServiceException;
42 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
43 import com.android.ondevicepersonalization.services.data.DataAccessPermission;
44 import com.android.ondevicepersonalization.services.data.DataAccessServiceImpl;
45 import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus;
46 import com.android.ondevicepersonalization.services.display.DisplayHelper;
47 import com.android.ondevicepersonalization.services.manifest.AppManifestConfigHelper;
48 import com.android.ondevicepersonalization.services.serviceflow.ServiceFlow;
49 import com.android.ondevicepersonalization.services.util.CryptUtils;
50 import com.android.ondevicepersonalization.services.util.DebugUtils;
51 import com.android.ondevicepersonalization.services.util.StatsUtils;
52 
53 import com.google.common.util.concurrent.FluentFuture;
54 import com.google.common.util.concurrent.FutureCallback;
55 import com.google.common.util.concurrent.Futures;
56 import com.google.common.util.concurrent.ListenableFuture;
57 import com.google.common.util.concurrent.ListeningExecutorService;
58 import com.google.common.util.concurrent.ListeningScheduledExecutorService;
59 
60 import java.util.Objects;
61 import java.util.concurrent.TimeUnit;
62 
63 /**
64  * Handles a surface package request from an app or SDK.
65  */
66 public class RenderFlow implements ServiceFlow<SurfacePackage> {
67     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
68     private static final String TAG = "RenderFlow";
69 
70     @VisibleForTesting
71     static class Injector {
getExecutor()72         ListeningExecutorService getExecutor() {
73             return OnDevicePersonalizationExecutors.getBackgroundExecutor();
74         }
75 
decryptToken(String slotResultToken)76         SlotWrapper decryptToken(String slotResultToken) throws Exception {
77             return (SlotWrapper) CryptUtils.decrypt(slotResultToken);
78         }
79 
getClock()80         Clock getClock() {
81             return MonotonicClock.getInstance();
82         }
83 
getFlags()84         Flags getFlags() {
85             return FlagsFactory.getFlags();
86         }
87 
getScheduledExecutor()88         ListeningScheduledExecutorService getScheduledExecutor() {
89             return OnDevicePersonalizationExecutors.getScheduledExecutor();
90         }
91     }
92 
93     @NonNull
94     private final String mSlotResultToken;
95     @NonNull
96     private final IBinder mHostToken;
97     @NonNull private final int mDisplayId;
98     @NonNull private final int mWidth;
99     @NonNull private final int mHeight;
100     @NonNull
101     private final IRequestSurfacePackageCallback mCallback;
102     @NonNull
103     private final Context mContext;
104     private final long mStartTimeMillis;
105     private final long mServiceEntryTimeMillis;
106     @NonNull
107     private final Injector mInjector;
108     @NonNull
109     private final DisplayHelper mDisplayHelper;
110     @NonNull
111     private ComponentName mService;
112     private SlotWrapper mSlotWrapper;
113     private long mStartServiceTimeMillis;
114 
RenderFlow( @onNull String slotResultToken, @NonNull IBinder hostToken, int displayId, int width, int height, @NonNull IRequestSurfacePackageCallback callback, @NonNull Context context, long startTimeMillis, long serviceEntryTimeMillis)115     public RenderFlow(
116             @NonNull String slotResultToken,
117             @NonNull IBinder hostToken,
118             int displayId,
119             int width,
120             int height,
121             @NonNull IRequestSurfacePackageCallback callback,
122             @NonNull Context context,
123             long startTimeMillis,
124             long serviceEntryTimeMillis) {
125         this(slotResultToken, hostToken, displayId, width, height,
126                 callback, context, startTimeMillis, serviceEntryTimeMillis,
127                 new Injector(),
128                 new DisplayHelper(context));
129     }
130 
131     @VisibleForTesting
RenderFlow( @onNull String slotResultToken, @NonNull IBinder hostToken, int displayId, int width, int height, @NonNull IRequestSurfacePackageCallback callback, @NonNull Context context, long startTimeMillis, long serviceEntryTimeMillis, @NonNull Injector injector, @NonNull DisplayHelper displayHelper)132     RenderFlow(
133             @NonNull String slotResultToken,
134             @NonNull IBinder hostToken,
135             int displayId,
136             int width,
137             int height,
138             @NonNull IRequestSurfacePackageCallback callback,
139             @NonNull Context context,
140             long startTimeMillis,
141             long serviceEntryTimeMillis,
142             @NonNull Injector injector,
143             @NonNull DisplayHelper displayHelper) {
144         sLogger.d(TAG + ": RenderFlow created.");
145         mSlotResultToken = Objects.requireNonNull(slotResultToken);
146         mHostToken = Objects.requireNonNull(hostToken);
147         mDisplayId = displayId;
148         mWidth = width;
149         mHeight = height;
150         mCallback = Objects.requireNonNull(callback);
151         mStartTimeMillis = startTimeMillis;
152         mServiceEntryTimeMillis = serviceEntryTimeMillis;
153         mInjector = Objects.requireNonNull(injector);
154         mContext = Objects.requireNonNull(context);
155         mDisplayHelper = Objects.requireNonNull(displayHelper);
156     }
157 
158     @Override
isServiceFlowReady()159     public boolean isServiceFlowReady() {
160         mStartServiceTimeMillis = mInjector.getClock().elapsedRealtime();
161 
162         try {
163             if (!UserPrivacyStatus.getInstance().isProtectedAudienceEnabled()) {
164                 sLogger.d(TAG + ": User control is not given for targeting.");
165                 sendErrorResult(Constants.STATUS_PERSONALIZATION_DISABLED, 0, null);
166                 return false;
167             }
168 
169             mSlotWrapper = Objects.requireNonNull(
170                     mInjector.decryptToken(mSlotResultToken));
171             String servicePackageName = Objects.requireNonNull(
172                     mSlotWrapper.getServicePackageName());
173             String serviceClassName = Objects.requireNonNull(
174                     AppManifestConfigHelper.getServiceNameFromOdpSettings(
175                             mContext, servicePackageName));
176             mService = ComponentName.createRelative(servicePackageName, serviceClassName);
177         } catch (Exception e) {
178             sendErrorResult(Constants.STATUS_INTERNAL_ERROR, 0, e);
179             return false;
180         }
181         return true;
182     }
183 
184     @Override
getService()185     public ComponentName getService() {
186         return mService;
187     }
188 
189     @Override
getServiceParams()190     public Bundle getServiceParams() {
191         RenderingConfig renderingConfig =
192                 Objects.requireNonNull(mSlotWrapper.getRenderingConfig());
193 
194         Bundle serviceParams = new Bundle();
195 
196         serviceParams.putParcelable(
197                 Constants.EXTRA_INPUT, new RenderInputParcel.Builder()
198                         .setHeight(mHeight)
199                         .setWidth(mWidth)
200                         .setRenderingConfig(renderingConfig)
201                         .build());
202         serviceParams.putBinder(
203                 Constants.EXTRA_DATA_ACCESS_SERVICE_BINDER, new DataAccessServiceImpl(
204                         mService, mContext, /* includeLocalData */ DataAccessPermission.DENIED,
205                         /* includeEventData */ DataAccessPermission.DENIED));
206 
207         return serviceParams;
208     }
209 
210     @Override
uploadServiceFlowMetrics(ListenableFuture<Bundle> runServiceFuture)211     public void uploadServiceFlowMetrics(ListenableFuture<Bundle> runServiceFuture) {
212         var unused =
213                 FluentFuture.from(runServiceFuture)
214                         .transform(
215                                 val -> {
216                                     StatsUtils.writeServiceRequestMetrics(
217                                             Constants.API_NAME_SERVICE_ON_RENDER,
218                                             mService.getPackageName(),
219                                             val,
220                                             mInjector.getClock(),
221                                             Constants.STATUS_SUCCESS,
222                                             mStartServiceTimeMillis);
223                                     return val;
224                                 },
225                                 mInjector.getExecutor())
226                         .catchingAsync(
227                                 Exception.class,
228                                 e -> {
229                                     StatsUtils.writeServiceRequestMetrics(
230                                             Constants.API_NAME_SERVICE_ON_RENDER,
231                                             mService.getPackageName(),
232                                             /* result= */ null,
233                                             mInjector.getClock(),
234                                             Constants.STATUS_INTERNAL_ERROR,
235                                             mStartServiceTimeMillis);
236                                     return Futures.immediateFailedFuture(e);
237                                 },
238                                 mInjector.getExecutor());
239     }
240 
241     @Override
getServiceFlowResultFuture( ListenableFuture<Bundle> runServiceFuture)242     public ListenableFuture<SurfacePackage> getServiceFlowResultFuture(
243             ListenableFuture<Bundle> runServiceFuture) {
244         RequestLogRecord logRecord = mSlotWrapper.getLogRecord();
245         long queryId = mSlotWrapper.getQueryId();
246 
247         return FluentFuture.from(runServiceFuture)
248                 .transform(
249                         result ->
250                                 result.getParcelable(
251                                         Constants.EXTRA_RESULT, RenderOutputParcel.class),
252                         mInjector.getExecutor())
253                 .transform(
254                         result -> mDisplayHelper.generateHtml(
255                                 result, mService),
256                         mInjector.getExecutor())
257                 .transformAsync(
258                         result -> mDisplayHelper.displayHtml(
259                                 result,
260                                 logRecord,
261                                 queryId,
262                                 mService,
263                                 mHostToken,
264                                 mDisplayId,
265                                 mWidth,
266                                 mHeight),
267                         mInjector.getExecutor())
268                 .withTimeout(
269                         mInjector.getFlags().getIsolatedServiceDeadlineSeconds(),
270                         TimeUnit.SECONDS,
271                         mInjector.getScheduledExecutor()
272                 );
273     }
274 
275     @Override
returnResultThroughCallback( ListenableFuture<SurfacePackage> serviceFlowResultFuture)276     public  void returnResultThroughCallback(
277             ListenableFuture<SurfacePackage> serviceFlowResultFuture) {
278         Futures.addCallback(
279                 serviceFlowResultFuture,
280                 new FutureCallback<>() {
281                     @Override
282                     public void onSuccess(SurfacePackage surfacePackage) {
283                         sendDisplayResult(surfacePackage);
284                     }
285 
286                     @Override
287                     public void onFailure(Throwable t) {
288                         sLogger.w(TAG + ": Request failed.", t);
289                         if (t instanceof OdpServiceException) {
290                             OdpServiceException e = (OdpServiceException) t;
291                             sendErrorResult(
292                                     e.getErrorCode(),
293                                     DebugUtils.getIsolatedServiceExceptionCode(
294                                             mContext, mService, e),
295                                     t);
296                         } else {
297                             sendErrorResult(Constants.STATUS_INTERNAL_ERROR, 0, t);
298                         }
299                     }
300                 },
301                 mInjector.getExecutor());
302     }
303 
304     @Override
cleanUpServiceParams()305     public void cleanUpServiceParams() {}
306 
sendDisplayResult(SurfacePackage surfacePackage)307     private void sendDisplayResult(SurfacePackage surfacePackage) {
308         if (surfacePackage != null) {
309             sendSuccessResult(surfacePackage);
310         } else {
311             sLogger.w(TAG + ": surfacePackages is null or empty");
312             sendErrorResult(
313                     Constants.STATUS_INTERNAL_ERROR,
314                     0,
315                     new IllegalStateException("missing surfacePackage"));
316         }
317     }
318 
sendSuccessResult(SurfacePackage surfacePackage)319     private void sendSuccessResult(SurfacePackage surfacePackage) {
320         int responseCode = Constants.STATUS_SUCCESS;
321         try {
322             mCallback.onSuccess(
323                     surfacePackage,
324                     new CalleeMetadata.Builder()
325                             .setServiceEntryTimeMillis(mServiceEntryTimeMillis)
326                             .setCallbackInvokeTimeMillis(SystemClock.elapsedRealtime()).build());
327         } catch (RemoteException e) {
328             responseCode = Constants.STATUS_INTERNAL_ERROR;
329             sLogger.w(TAG + ": Callback error", e);
330         }
331     }
332 
sendErrorResult(int errorCode, int isolatedServiceErrorCode, Throwable t)333     private void sendErrorResult(int errorCode, int isolatedServiceErrorCode, Throwable t) {
334         try {
335             mCallback.onError(
336                     errorCode,
337                     isolatedServiceErrorCode,
338                     DebugUtils.serializeExceptionInfo(mService, t),
339                     new CalleeMetadata.Builder()
340                             .setServiceEntryTimeMillis(mServiceEntryTimeMillis)
341                             .setCallbackInvokeTimeMillis(SystemClock.elapsedRealtime()).build());
342         } catch (RemoteException e) {
343             sLogger.w(TAG + ": Callback error", e);
344         }
345     }
346 }
347