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); 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); 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 = FluentFuture.from(runServiceFuture) 213 .transform( 214 val -> { 215 StatsUtils.writeServiceRequestMetrics( 216 Constants.API_NAME_SERVICE_ON_RENDER, 217 val, mInjector.getClock(), 218 Constants.STATUS_SUCCESS, mStartServiceTimeMillis); 219 return val; 220 }, 221 mInjector.getExecutor() 222 ) 223 .catchingAsync( 224 Exception.class, 225 e -> { 226 StatsUtils.writeServiceRequestMetrics( 227 Constants.API_NAME_SERVICE_ON_RENDER, /* result= */ null, 228 mInjector.getClock(), 229 Constants.STATUS_INTERNAL_ERROR, mStartServiceTimeMillis); 230 return Futures.immediateFailedFuture(e); 231 }, 232 mInjector.getExecutor() 233 ); 234 } 235 236 @Override getServiceFlowResultFuture( ListenableFuture<Bundle> runServiceFuture)237 public ListenableFuture<SurfacePackage> getServiceFlowResultFuture( 238 ListenableFuture<Bundle> runServiceFuture) { 239 RequestLogRecord logRecord = mSlotWrapper.getLogRecord(); 240 long queryId = mSlotWrapper.getQueryId(); 241 242 return FluentFuture.from(runServiceFuture) 243 .transform( 244 result -> 245 result.getParcelable( 246 Constants.EXTRA_RESULT, RenderOutputParcel.class), 247 mInjector.getExecutor()) 248 .transform( 249 result -> mDisplayHelper.generateHtml( 250 result, mService), 251 mInjector.getExecutor()) 252 .transformAsync( 253 result -> mDisplayHelper.displayHtml( 254 result, 255 logRecord, 256 queryId, 257 mService, 258 mHostToken, 259 mDisplayId, 260 mWidth, 261 mHeight), 262 mInjector.getExecutor()) 263 .withTimeout( 264 mInjector.getFlags().getIsolatedServiceDeadlineSeconds(), 265 TimeUnit.SECONDS, 266 mInjector.getScheduledExecutor() 267 ); 268 } 269 270 @Override returnResultThroughCallback( ListenableFuture<SurfacePackage> serviceFlowResultFuture)271 public void returnResultThroughCallback( 272 ListenableFuture<SurfacePackage> serviceFlowResultFuture) { 273 Futures.addCallback( 274 serviceFlowResultFuture, 275 new FutureCallback<>() { 276 @Override 277 public void onSuccess(SurfacePackage surfacePackage) { 278 sendDisplayResult(surfacePackage); 279 } 280 281 @Override 282 public void onFailure(Throwable t) { 283 sLogger.w(TAG + ": Request failed.", t); 284 if (t instanceof OdpServiceException) { 285 OdpServiceException e = (OdpServiceException) t; 286 sendErrorResult( 287 e.getErrorCode(), 288 DebugUtils.getIsolatedServiceExceptionCode( 289 mContext, mService, e)); 290 } else { 291 sendErrorResult(Constants.STATUS_INTERNAL_ERROR, t); 292 } 293 } 294 }, 295 mInjector.getExecutor()); 296 } 297 298 @Override cleanUpServiceParams()299 public void cleanUpServiceParams() {} 300 sendDisplayResult(SurfacePackage surfacePackage)301 private void sendDisplayResult(SurfacePackage surfacePackage) { 302 if (surfacePackage != null) { 303 sendSuccessResult(surfacePackage); 304 } else { 305 sLogger.w(TAG + ": surfacePackages is null or empty"); 306 sendErrorResult(Constants.STATUS_INTERNAL_ERROR, 0); 307 } 308 } 309 sendSuccessResult(SurfacePackage surfacePackage)310 private void sendSuccessResult(SurfacePackage surfacePackage) { 311 int responseCode = Constants.STATUS_SUCCESS; 312 try { 313 mCallback.onSuccess( 314 surfacePackage, 315 new CalleeMetadata.Builder() 316 .setServiceEntryTimeMillis(mServiceEntryTimeMillis) 317 .setCallbackInvokeTimeMillis(SystemClock.elapsedRealtime()).build()); 318 } catch (RemoteException e) { 319 responseCode = Constants.STATUS_INTERNAL_ERROR; 320 sLogger.w(TAG + ": Callback error", e); 321 } 322 } 323 sendErrorResult(int errorCode, int isolatedServiceErrorCode)324 private void sendErrorResult(int errorCode, int isolatedServiceErrorCode) { 325 try { 326 mCallback.onError( 327 errorCode, 328 isolatedServiceErrorCode, 329 null, 330 new CalleeMetadata.Builder() 331 .setServiceEntryTimeMillis(mServiceEntryTimeMillis) 332 .setCallbackInvokeTimeMillis(SystemClock.elapsedRealtime()).build()); 333 } catch (RemoteException e) { 334 sLogger.w(TAG + ": Callback error", e); 335 } 336 } 337 sendErrorResult(int errorCode, Throwable t)338 private void sendErrorResult(int errorCode, Throwable t) { 339 try { 340 mCallback.onError( 341 errorCode, 342 0, 343 DebugUtils.getErrorMessage(mContext, t), 344 new CalleeMetadata.Builder() 345 .setServiceEntryTimeMillis(mServiceEntryTimeMillis) 346 .setCallbackInvokeTimeMillis(SystemClock.elapsedRealtime()).build()); 347 } catch (RemoteException e) { 348 sLogger.w(TAG + ": Callback error", e); 349 } 350 } 351 } 352