• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }