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.services; 18 19 import static android.adservices.ondevicepersonalization.OnDevicePersonalizationPermissions.NOTIFY_MEASUREMENT_EVENT; 20 21 import android.adservices.ondevicepersonalization.CallerMetadata; 22 import android.adservices.ondevicepersonalization.Constants; 23 import android.adservices.ondevicepersonalization.ExecuteInIsolatedServiceRequest; 24 import android.adservices.ondevicepersonalization.ExecuteOptionsParcel; 25 import android.adservices.ondevicepersonalization.aidl.IExecuteCallback; 26 import android.adservices.ondevicepersonalization.aidl.IIsFeatureEnabledCallback; 27 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService; 28 import android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback; 29 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback; 30 import android.annotation.NonNull; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.pm.PackageManager; 34 import android.os.Binder; 35 import android.os.Bundle; 36 import android.os.IBinder; 37 import android.os.SystemClock; 38 import android.os.Trace; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.odp.module.common.DeviceUtils; 42 import com.android.odp.module.common.ProcessWrapper; 43 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 44 import com.android.ondevicepersonalization.services.enrollment.PartnerEnrollmentChecker; 45 import com.android.ondevicepersonalization.services.serviceflow.ServiceFlowOrchestrator; 46 import com.android.ondevicepersonalization.services.serviceflow.ServiceFlowType; 47 import com.android.ondevicepersonalization.services.statsd.ApiCallStats; 48 import com.android.ondevicepersonalization.services.statsd.OdpStatsdLogger; 49 50 import java.util.Objects; 51 52 /** Implementation of OnDevicePersonalizationManagingService */ 53 public class OnDevicePersonalizationManagingServiceDelegate 54 extends IOnDevicePersonalizationManagingService.Stub { 55 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 56 private static final String TAG = "OnDevicePersonalizationManagingServiceDelegate"; 57 private static final ServiceFlowOrchestrator sSfo = ServiceFlowOrchestrator.getInstance(); 58 @NonNull private final Context mContext; 59 private final Injector mInjector; 60 OnDevicePersonalizationManagingServiceDelegate(@onNull Context context)61 public OnDevicePersonalizationManagingServiceDelegate(@NonNull Context context) { 62 this(context, new Injector()); 63 } 64 65 @VisibleForTesting OnDevicePersonalizationManagingServiceDelegate( @onNull Context context, Injector injector)66 public OnDevicePersonalizationManagingServiceDelegate( 67 @NonNull Context context, Injector injector) { 68 mContext = Objects.requireNonNull(context); 69 mInjector = injector; 70 } 71 72 static class Injector { getFlags()73 Flags getFlags() { 74 return FlagsFactory.getFlags(); 75 } 76 } 77 78 @Override getVersion()79 public String getVersion() { 80 return "1.0"; 81 } 82 83 @Override execute( @onNull String callingPackageName, @NonNull ComponentName handler, @NonNull Bundle wrappedParams, @NonNull CallerMetadata metadata, @NonNull ExecuteOptionsParcel options, @NonNull IExecuteCallback callback)84 public void execute( 85 @NonNull String callingPackageName, 86 @NonNull ComponentName handler, 87 @NonNull Bundle wrappedParams, 88 @NonNull CallerMetadata metadata, 89 @NonNull ExecuteOptionsParcel options, 90 @NonNull IExecuteCallback callback) { 91 if (getGlobalKillSwitch()) { 92 throw new IllegalStateException("Service skipped as the global kill switch is on."); 93 } 94 95 if (!DeviceUtils.isOdpSupported(mContext)) { 96 throw new IllegalStateException("Device not supported."); 97 } 98 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 99 100 Trace.beginSection("OdpManagingServiceDelegate#Execute"); 101 Objects.requireNonNull(callingPackageName); 102 Objects.requireNonNull(handler); 103 Objects.requireNonNull(handler.getPackageName()); 104 Objects.requireNonNull(handler.getClassName()); 105 Objects.requireNonNull(wrappedParams); 106 Objects.requireNonNull(metadata); 107 Objects.requireNonNull(callback); 108 if (callingPackageName.isEmpty()) { 109 throw new IllegalArgumentException("missing app package name"); 110 } 111 if (handler.getPackageName().isEmpty()) { 112 throw new IllegalArgumentException("missing service package name"); 113 } 114 if (handler.getClassName().isEmpty()) { 115 throw new IllegalArgumentException("missing service class name"); 116 } 117 118 checkExecutionsOptions(options); 119 120 final int uid = Binder.getCallingUid(); 121 enforceCallingPackageBelongsToUid(callingPackageName, uid); 122 enforceEnrollment(callingPackageName, handler); 123 124 sSfo.schedule( 125 ServiceFlowType.APP_REQUEST_FLOW, 126 callingPackageName, 127 handler, 128 wrappedParams, 129 callback, 130 mContext, 131 metadata.getStartTimeMillis(), 132 serviceEntryTimeMillis, 133 options); 134 Trace.endSection(); 135 } 136 137 @Override requestSurfacePackage( @onNull String slotResultToken, @NonNull IBinder hostToken, int displayId, int width, int height, @NonNull CallerMetadata metadata, @NonNull IRequestSurfacePackageCallback callback)138 public void requestSurfacePackage( 139 @NonNull String slotResultToken, 140 @NonNull IBinder hostToken, 141 int displayId, 142 int width, 143 int height, 144 @NonNull CallerMetadata metadata, 145 @NonNull IRequestSurfacePackageCallback callback) { 146 if (getGlobalKillSwitch()) { 147 throw new IllegalStateException("Service skipped as the global kill switch is on."); 148 } 149 150 if (!DeviceUtils.isOdpSupported(mContext)) { 151 throw new IllegalStateException("Device not supported."); 152 } 153 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 154 155 Trace.beginSection("OdpManagingServiceDelegate#RequestSurfacePackage"); 156 Objects.requireNonNull(slotResultToken); 157 Objects.requireNonNull(hostToken); 158 Objects.requireNonNull(callback); 159 if (width <= 0) { 160 throw new IllegalArgumentException("width must be > 0"); 161 } 162 163 if (height <= 0) { 164 throw new IllegalArgumentException("height must be > 0"); 165 } 166 167 if (displayId < 0) { 168 throw new IllegalArgumentException("displayId must be >= 0"); 169 } 170 171 sSfo.schedule(ServiceFlowType.RENDER_FLOW, 172 slotResultToken, hostToken, displayId, 173 width, height, callback, 174 mContext, metadata.getStartTimeMillis(), serviceEntryTimeMillis); 175 Trace.endSection(); 176 } 177 178 // TODO(b/301732670): Move to a new service. 179 @Override registerMeasurementEvent( @onNull int measurementEventType, @NonNull Bundle params, @NonNull CallerMetadata metadata, @NonNull IRegisterMeasurementEventCallback callback )180 public void registerMeasurementEvent( 181 @NonNull int measurementEventType, 182 @NonNull Bundle params, 183 @NonNull CallerMetadata metadata, 184 @NonNull IRegisterMeasurementEventCallback callback 185 ) { 186 if (getGlobalKillSwitch()) { 187 throw new IllegalStateException("Service skipped as the global kill switch is on."); 188 } 189 190 if (!DeviceUtils.isOdpSupported(mContext)) { 191 throw new IllegalStateException("Device not supported."); 192 } 193 194 if (mContext.checkCallingPermission(NOTIFY_MEASUREMENT_EVENT) 195 != PackageManager.PERMISSION_GRANTED) { 196 throw new SecurityException("Permission denied: " + NOTIFY_MEASUREMENT_EVENT); 197 } 198 199 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 200 Trace.beginSection("OdpManagingServiceDelegate#RegisterMeasurementEvent"); 201 if (measurementEventType 202 != Constants.MEASUREMENT_EVENT_TYPE_WEB_TRIGGER) { 203 throw new IllegalStateException("invalid measurementEventType"); 204 } 205 Objects.requireNonNull(params); 206 Objects.requireNonNull(metadata); 207 Objects.requireNonNull(callback); 208 209 sSfo.schedule(ServiceFlowType.WEB_TRIGGER_FLOW, 210 params, mContext, 211 callback, metadata.getStartTimeMillis(), serviceEntryTimeMillis); 212 Trace.endSection(); 213 } 214 215 @Override isFeatureEnabled( @onNull String featureName, @NonNull CallerMetadata metadata, @NonNull IIsFeatureEnabledCallback callback)216 public void isFeatureEnabled( 217 @NonNull String featureName, 218 @NonNull CallerMetadata metadata, 219 @NonNull IIsFeatureEnabledCallback callback) { 220 if (getGlobalKillSwitch()) { 221 throw new IllegalStateException("Service skipped as the global kill switch is on."); 222 } 223 224 if (!DeviceUtils.isOdpSupported(mContext)) { 225 throw new IllegalStateException("Device not supported."); 226 } 227 228 if (!getOdpIsFeatureEnabledFlagEnabled()) { 229 throw new IllegalStateException("isFeatureEnabled flag is not enabled."); 230 } 231 232 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 233 Trace.beginSection("OdpManagingServiceDelegate#IsFeatureEnabled"); 234 235 FeatureStatusManager.getFeatureStatusAndSendResult(featureName, 236 serviceEntryTimeMillis, 237 callback); 238 239 Trace.endSection(); 240 } 241 242 @Override logApiCallStats( String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)243 public void logApiCallStats( 244 String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, 245 long rpcReturnLatencyMillis, int responseCode) { 246 final int uid = Binder.getCallingUid(); 247 OnDevicePersonalizationExecutors.getBackgroundExecutor() 248 .execute( 249 () -> 250 handleLogApiCallStats(uid, sdkPackageName, apiName, latencyMillis, 251 rpcCallLatencyMillis, rpcReturnLatencyMillis, 252 responseCode)); 253 } 254 handleLogApiCallStats(int appUid, String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)255 private void handleLogApiCallStats(int appUid, String sdkPackageName, int apiName, 256 long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, 257 int responseCode) { 258 try { 259 OdpStatsdLogger.getInstance() 260 .logApiCallStats( 261 new ApiCallStats.Builder(apiName) 262 .setResponseCode(responseCode) 263 .setAppUid(appUid) 264 .setSdkPackageName(sdkPackageName == null ? "" : sdkPackageName) 265 .setLatencyMillis((int) latencyMillis) 266 .setRpcCallLatencyMillis((int) rpcCallLatencyMillis) 267 .setRpcReturnLatencyMillis((int) rpcReturnLatencyMillis) 268 .build()); 269 } catch (Exception e) { 270 sLogger.e(e, TAG + ": error logging api call stats"); 271 } 272 } 273 getGlobalKillSwitch()274 private boolean getGlobalKillSwitch() { 275 long origId = Binder.clearCallingIdentity(); 276 boolean globalKillSwitch = mInjector.getFlags().getGlobalKillSwitch(); 277 Binder.restoreCallingIdentity(origId); 278 return globalKillSwitch; 279 } 280 getOdpIsFeatureEnabledFlagEnabled()281 private boolean getOdpIsFeatureEnabledFlagEnabled() { 282 long origId = Binder.clearCallingIdentity(); 283 boolean flagEnabled = mInjector.getFlags().isFeatureEnabledApiEnabled(); 284 Binder.restoreCallingIdentity(origId); 285 return flagEnabled; 286 } 287 288 @VisibleForTesting enforceCallingPackageBelongsToUid(@onNull String packageName, int uid)289 void enforceCallingPackageBelongsToUid(@NonNull String packageName, int uid) { 290 int packageUid; 291 PackageManager pm = mContext.getPackageManager(); 292 try { 293 packageUid = pm.getPackageUid(packageName, 0); 294 } catch (PackageManager.NameNotFoundException e) { 295 throw new SecurityException(packageName + " not found"); 296 } 297 298 int appUid = ProcessWrapper.isSdkSandboxUid(uid) 299 ? ProcessWrapper.getAppUidForSdkSandboxUid(uid) : uid; 300 if (packageUid != appUid) { 301 throw new SecurityException(packageName + " does not belong to uid " + uid); 302 } 303 } 304 enforceEnrollment(@onNull String callingPackageName, @NonNull ComponentName service)305 private void enforceEnrollment(@NonNull String callingPackageName, 306 @NonNull ComponentName service) { 307 long origId = Binder.clearCallingIdentity(); 308 309 try { 310 if (!PartnerEnrollmentChecker.isCallerAppEnrolled(callingPackageName)) { 311 sLogger.d("caller app %s not enrolled to call ODP.", callingPackageName); 312 throw new IllegalStateException( 313 "Service skipped as the caller app is not enrolled to call ODP."); 314 } 315 if (!PartnerEnrollmentChecker.isIsolatedServiceEnrolled(service.getPackageName())) { 316 sLogger.d("isolated service %s not enrolled to access ODP.", 317 service.getPackageName()); 318 throw new IllegalStateException( 319 "Service skipped as the isolated service is not enrolled to access ODP."); 320 } 321 } finally { 322 Binder.restoreCallingIdentity(origId); 323 } 324 } 325 checkExecutionsOptions(@onNull ExecuteOptionsParcel options)326 private void checkExecutionsOptions(@NonNull ExecuteOptionsParcel options) { 327 long origId = Binder.clearCallingIdentity(); 328 try { 329 if (options.getOutputType() 330 == ExecuteInIsolatedServiceRequest.OutputSpec.OUTPUT_TYPE_BEST_VALUE 331 && options.getMaxIntValue() > mInjector.getFlags().getMaxIntValuesLimit()) { 332 throw new IllegalArgumentException( 333 "The maxIntValue in OutputSpec can not exceed limit " 334 + mInjector.getFlags().getMaxIntValuesLimit()); 335 } 336 } finally { 337 Binder.restoreCallingIdentity(origId); 338 } 339 } 340 }