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