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.aidl.IExecuteCallback; 24 import android.adservices.ondevicepersonalization.aidl.IOnDevicePersonalizationManagingService; 25 import android.adservices.ondevicepersonalization.aidl.IRegisterMeasurementEventCallback; 26 import android.adservices.ondevicepersonalization.aidl.IRequestSurfacePackageCallback; 27 import android.annotation.NonNull; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.pm.PackageManager; 31 import android.os.Binder; 32 import android.os.Bundle; 33 import android.os.IBinder; 34 import android.os.SystemClock; 35 import android.os.Trace; 36 37 import com.android.odp.module.common.DeviceUtils; 38 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 39 import com.android.ondevicepersonalization.services.enrollment.PartnerEnrollmentChecker; 40 import com.android.ondevicepersonalization.services.serviceflow.ServiceFlowOrchestrator; 41 import com.android.ondevicepersonalization.services.serviceflow.ServiceFlowType; 42 import com.android.ondevicepersonalization.services.statsd.ApiCallStats; 43 import com.android.ondevicepersonalization.services.statsd.OdpStatsdLogger; 44 45 import java.util.Objects; 46 47 /** Implementation of OnDevicePersonalizationManagingService */ 48 public class OnDevicePersonalizationManagingServiceDelegate 49 extends IOnDevicePersonalizationManagingService.Stub { 50 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 51 private static final String TAG = "OnDevicePersonalizationManagingServiceDelegate"; 52 private static final ServiceFlowOrchestrator sSfo = ServiceFlowOrchestrator.getInstance(); 53 @NonNull private final Context mContext; 54 OnDevicePersonalizationManagingServiceDelegate(@onNull Context context)55 public OnDevicePersonalizationManagingServiceDelegate(@NonNull Context context) { 56 mContext = Objects.requireNonNull(context); 57 } 58 59 @Override getVersion()60 public String getVersion() { 61 return "1.0"; 62 } 63 64 @Override execute( @onNull String callingPackageName, @NonNull ComponentName handler, @NonNull Bundle wrappedParams, @NonNull CallerMetadata metadata, @NonNull IExecuteCallback callback)65 public void execute( 66 @NonNull String callingPackageName, 67 @NonNull ComponentName handler, 68 @NonNull Bundle wrappedParams, 69 @NonNull CallerMetadata metadata, 70 @NonNull IExecuteCallback callback) { 71 if (getGlobalKillSwitch()) { 72 throw new IllegalStateException("Service skipped as the global kill switch is on."); 73 } 74 75 if (!DeviceUtils.isOdpSupported(mContext)) { 76 throw new IllegalStateException("Device not supported."); 77 } 78 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 79 80 Trace.beginSection("OdpManagingServiceDelegate#Execute"); 81 Objects.requireNonNull(callingPackageName); 82 Objects.requireNonNull(handler); 83 Objects.requireNonNull(handler.getPackageName()); 84 Objects.requireNonNull(handler.getClassName()); 85 Objects.requireNonNull(wrappedParams); 86 Objects.requireNonNull(metadata); 87 Objects.requireNonNull(callback); 88 if (callingPackageName.isEmpty()) { 89 throw new IllegalArgumentException("missing app package name"); 90 } 91 if (handler.getPackageName().isEmpty()) { 92 throw new IllegalArgumentException("missing service package name"); 93 } 94 if (handler.getClassName().isEmpty()) { 95 throw new IllegalArgumentException("missing service class name"); 96 } 97 98 final int uid = Binder.getCallingUid(); 99 enforceCallingPackageBelongsToUid(callingPackageName, uid); 100 enforceEnrollment(callingPackageName, handler); 101 102 sSfo.schedule(ServiceFlowType.APP_REQUEST_FLOW, 103 callingPackageName, handler, wrappedParams, 104 callback, mContext, metadata.getStartTimeMillis(), serviceEntryTimeMillis); 105 Trace.endSection(); 106 } 107 108 @Override requestSurfacePackage( @onNull String slotResultToken, @NonNull IBinder hostToken, int displayId, int width, int height, @NonNull CallerMetadata metadata, @NonNull IRequestSurfacePackageCallback callback)109 public void requestSurfacePackage( 110 @NonNull String slotResultToken, 111 @NonNull IBinder hostToken, 112 int displayId, 113 int width, 114 int height, 115 @NonNull CallerMetadata metadata, 116 @NonNull IRequestSurfacePackageCallback callback) { 117 if (getGlobalKillSwitch()) { 118 throw new IllegalStateException("Service skipped as the global kill switch is on."); 119 } 120 121 if (!DeviceUtils.isOdpSupported(mContext)) { 122 throw new IllegalStateException("Device not supported."); 123 } 124 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 125 126 Trace.beginSection("OdpManagingServiceDelegate#RequestSurfacePackage"); 127 Objects.requireNonNull(slotResultToken); 128 Objects.requireNonNull(hostToken); 129 Objects.requireNonNull(callback); 130 if (width <= 0) { 131 throw new IllegalArgumentException("width must be > 0"); 132 } 133 134 if (height <= 0) { 135 throw new IllegalArgumentException("height must be > 0"); 136 } 137 138 if (displayId < 0) { 139 throw new IllegalArgumentException("displayId must be >= 0"); 140 } 141 142 sSfo.schedule(ServiceFlowType.RENDER_FLOW, 143 slotResultToken, hostToken, displayId, 144 width, height, callback, 145 mContext, metadata.getStartTimeMillis(), serviceEntryTimeMillis); 146 Trace.endSection(); 147 } 148 149 // TODO(b/301732670): Move to a new service. 150 @Override registerMeasurementEvent( @onNull int measurementEventType, @NonNull Bundle params, @NonNull CallerMetadata metadata, @NonNull IRegisterMeasurementEventCallback callback )151 public void registerMeasurementEvent( 152 @NonNull int measurementEventType, 153 @NonNull Bundle params, 154 @NonNull CallerMetadata metadata, 155 @NonNull IRegisterMeasurementEventCallback callback 156 ) { 157 if (getGlobalKillSwitch()) { 158 throw new IllegalStateException("Service skipped as the global kill switch is on."); 159 } 160 161 if (!DeviceUtils.isOdpSupported(mContext)) { 162 throw new IllegalStateException("Device not supported."); 163 } 164 165 if (mContext.checkCallingPermission(NOTIFY_MEASUREMENT_EVENT) 166 != PackageManager.PERMISSION_GRANTED) { 167 throw new SecurityException("Permission denied: " + NOTIFY_MEASUREMENT_EVENT); 168 } 169 170 long serviceEntryTimeMillis = SystemClock.elapsedRealtime(); 171 Trace.beginSection("OdpManagingServiceDelegate#RegisterMeasurementEvent"); 172 if (measurementEventType 173 != Constants.MEASUREMENT_EVENT_TYPE_WEB_TRIGGER) { 174 throw new IllegalStateException("invalid measurementEventType"); 175 } 176 Objects.requireNonNull(params); 177 Objects.requireNonNull(metadata); 178 Objects.requireNonNull(callback); 179 180 sSfo.schedule(ServiceFlowType.WEB_TRIGGER_FLOW, 181 params, mContext, 182 callback, metadata.getStartTimeMillis(), serviceEntryTimeMillis); 183 Trace.endSection(); 184 } 185 186 @Override logApiCallStats( String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)187 public void logApiCallStats( 188 String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, 189 long rpcReturnLatencyMillis, int responseCode) { 190 final int uid = Binder.getCallingUid(); 191 OnDevicePersonalizationExecutors.getBackgroundExecutor() 192 .execute( 193 () -> 194 handleLogApiCallStats(uid, sdkPackageName, apiName, latencyMillis, 195 rpcCallLatencyMillis, rpcReturnLatencyMillis, 196 responseCode)); 197 } 198 handleLogApiCallStats(int appUid, String sdkPackageName, int apiName, long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, int responseCode)199 private void handleLogApiCallStats(int appUid, String sdkPackageName, int apiName, 200 long latencyMillis, long rpcCallLatencyMillis, long rpcReturnLatencyMillis, 201 int responseCode) { 202 try { 203 OdpStatsdLogger.getInstance() 204 .logApiCallStats( 205 new ApiCallStats.Builder(apiName) 206 .setResponseCode(responseCode) 207 .setAppUid(appUid) 208 .setSdkPackageName(sdkPackageName == null ? "" : sdkPackageName) 209 .setLatencyMillis((int) latencyMillis) 210 .setRpcCallLatencyMillis((int) rpcCallLatencyMillis) 211 .setRpcReturnLatencyMillis((int) rpcReturnLatencyMillis) 212 .build()); 213 } catch (Exception e) { 214 sLogger.e(e, TAG + ": error logging api call stats"); 215 } 216 } 217 getGlobalKillSwitch()218 private boolean getGlobalKillSwitch() { 219 long origId = Binder.clearCallingIdentity(); 220 boolean globalKillSwitch = FlagsFactory.getFlags().getGlobalKillSwitch(); 221 FlagsFactory.getFlags().setStableFlags(); 222 Binder.restoreCallingIdentity(origId); 223 return globalKillSwitch; 224 } 225 enforceCallingPackageBelongsToUid(@onNull String packageName, int uid)226 private void enforceCallingPackageBelongsToUid(@NonNull String packageName, int uid) { 227 int packageUid; 228 PackageManager pm = mContext.getPackageManager(); 229 try { 230 packageUid = pm.getPackageUid(packageName, 0); 231 } catch (PackageManager.NameNotFoundException e) { 232 throw new SecurityException(packageName + " not found"); 233 } 234 if (packageUid != uid) { 235 throw new SecurityException(packageName + " does not belong to uid " + uid); 236 } 237 //TODO(b/242792629): Handle requests from the SDK sandbox. 238 } 239 enforceEnrollment(@onNull String callingPackageName, @NonNull ComponentName service)240 private void enforceEnrollment(@NonNull String callingPackageName, 241 @NonNull ComponentName service) { 242 long origId = Binder.clearCallingIdentity(); 243 244 try { 245 if (!PartnerEnrollmentChecker.isCallerAppEnrolled(callingPackageName)) { 246 sLogger.d("caller app %s not enrolled to call ODP.", callingPackageName); 247 throw new IllegalStateException( 248 "Service skipped as the caller app is not enrolled to call ODP."); 249 } 250 if (!PartnerEnrollmentChecker.isIsolatedServiceEnrolled(service.getPackageName())) { 251 sLogger.d("isolated service %s not enrolled to access ODP.", 252 service.getPackageName()); 253 throw new IllegalStateException( 254 "Service skipped as the isolated service is not enrolled to access ODP."); 255 } 256 } finally { 257 Binder.restoreCallingIdentity(origId); 258 } 259 } 260 }