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 }