• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.federatedcompute;
18 
19 import android.adservices.ondevicepersonalization.Constants;
20 import android.adservices.ondevicepersonalization.aidl.IFederatedComputeCallback;
21 import android.adservices.ondevicepersonalization.aidl.IFederatedComputeService;
22 import android.annotation.NonNull;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.federatedcompute.FederatedComputeManager;
26 import android.federatedcompute.common.ClientConstants;
27 import android.federatedcompute.common.ScheduleFederatedComputeRequest;
28 import android.federatedcompute.common.TrainingOptions;
29 import android.os.OutcomeReceiver;
30 import android.os.RemoteException;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.ondevicepersonalization.internal.util.LoggerFactory;
34 import com.android.ondevicepersonalization.services.OnDevicePersonalizationExecutors;
35 import com.android.ondevicepersonalization.services.data.events.EventState;
36 import com.android.ondevicepersonalization.services.data.events.EventsDao;
37 import com.android.ondevicepersonalization.services.data.user.UserPrivacyStatus;
38 import com.android.ondevicepersonalization.services.manifest.AppManifestConfigHelper;
39 import com.android.ondevicepersonalization.services.util.DebugUtils;
40 
41 import com.google.common.util.concurrent.ListeningExecutorService;
42 
43 import java.io.IOException;
44 import java.util.Objects;
45 
46 /**
47  * A class that exports methods that adopter code in the isolated process can use to schedule/cancel
48  * federatedCompute jobs.
49  *
50  * <p>See {@link android.adservices.ondevicepersonalization.FederatedComputeScheduler#schedule}
51  */
52 public class FederatedComputeServiceImpl extends IFederatedComputeService.Stub {
53     private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
54     private static final String TAG = FederatedComputeServiceImpl.class.getSimpleName();
55 
56     @NonNull private final Context mApplicationContext;
57     @NonNull private final ComponentName mCallingService;
58     @NonNull private final Injector mInjector;
59 
60     @NonNull private final FederatedComputeManager mFederatedComputeManager;
61 
FederatedComputeServiceImpl( @onNull ComponentName service, @NonNull Context applicationContext)62     public FederatedComputeServiceImpl(
63             @NonNull ComponentName service, @NonNull Context applicationContext) {
64         this(service, applicationContext, new Injector());
65     }
66 
67     @VisibleForTesting
FederatedComputeServiceImpl( @onNull ComponentName service, @NonNull Context applicationContext, @NonNull Injector injector)68     FederatedComputeServiceImpl(
69             @NonNull ComponentName service,
70             @NonNull Context applicationContext,
71             @NonNull Injector injector) {
72         this.mApplicationContext = Objects.requireNonNull(applicationContext);
73         this.mCallingService = Objects.requireNonNull(service);
74         this.mInjector = Objects.requireNonNull(injector);
75         this.mFederatedComputeManager =
76                 Objects.requireNonNull(injector.getFederatedComputeManager(mApplicationContext));
77     }
78 
79     @Override
schedule(TrainingOptions trainingOptions, IFederatedComputeCallback callback)80     public void schedule(TrainingOptions trainingOptions, IFederatedComputeCallback callback) {
81         mInjector.getExecutor().execute(() -> handleSchedule(trainingOptions, callback));
82     }
83 
handleSchedule( TrainingOptions trainingOptions, IFederatedComputeCallback callback)84     private void handleSchedule(
85             TrainingOptions trainingOptions, IFederatedComputeCallback callback) {
86         try {
87             if (!UserPrivacyStatus.getInstance().isMeasurementEnabled()) {
88                 sLogger.d(TAG + ": measurement control is revoked.");
89                 sendError(callback, Constants.STATUS_PERSONALIZATION_DISABLED);
90                 return;
91             }
92 
93             String url =
94                     AppManifestConfigHelper.getFcRemoteServerUrlFromOdpSettings(
95                             mApplicationContext, mCallingService.getPackageName());
96             String overrideUrl =
97                     DebugUtils.getFcServerOverrideUrl(
98                             mApplicationContext, mCallingService.getPackageName());
99             if (!overrideUrl.isEmpty()) {
100                 url = overrideUrl;
101             }
102 
103             if (url == null) {
104                 sLogger.e(
105                         TAG
106                                 + ": Missing remote server URL for package: "
107                                 + mCallingService.getPackageName());
108                 sendError(callback, Constants.STATUS_FCP_MANIFEST_INVALID);
109                 return;
110             }
111 
112             ContextData contextData =
113                     new ContextData(
114                             mCallingService.getPackageName(), mCallingService.getClassName());
115             TrainingOptions trainingOptionsWithContext =
116                     new TrainingOptions.Builder()
117                             .setContextData(ContextData.toByteArray(contextData))
118                             .setTrainingInterval(trainingOptions.getTrainingInterval())
119                             .setPopulationName(trainingOptions.getPopulationName())
120                             .setServerAddress(url)
121                             .setOwnerComponentName(mCallingService)
122                             .build();
123             ScheduleFederatedComputeRequest request =
124                     new ScheduleFederatedComputeRequest.Builder()
125                             .setTrainingOptions(trainingOptionsWithContext)
126                             .build();
127             mFederatedComputeManager.schedule(
128                     request,
129                     mInjector.getExecutor(),
130                     new OutcomeReceiver<>() {
131                         @Override
132                         public void onResult(Object result) {
133                             mInjector
134                                     .getEventsDao(mApplicationContext)
135                                     .updateOrInsertEventState(
136                                             new EventState.Builder()
137                                                     .setService(mCallingService)
138                                                     .setTaskIdentifier(
139                                                             trainingOptions.getPopulationName())
140                                                     .setToken(new byte[] {})
141                                                     .build());
142                             sendSuccess(callback);
143                         }
144 
145                         @Override
146                         public void onError(Exception e) {
147                             sLogger.e(TAG + ": Error while scheduling federatedCompute", e);
148                             sendError(callback);
149                         }
150                     });
151         } catch (IOException | IllegalArgumentException e) {
152             // The AppManifestConfigHelper methods throw IllegalArgumentExceptions when
153             // parsings fails or the fc settings URL is missing.
154             sLogger.e(TAG + ": Error while scheduling federatedCompute", e);
155             sendError(callback, Constants.STATUS_FCP_MANIFEST_INVALID);
156         }
157     }
158 
159     @Override
cancel(String populationName, IFederatedComputeCallback callback)160     public void cancel(String populationName, IFederatedComputeCallback callback) {
161         EventState eventState =
162                 mInjector
163                         .getEventsDao(mApplicationContext)
164                         .getEventState(populationName, mCallingService);
165         if (eventState == null) {
166             sLogger.d(
167                     TAG
168                             + ": No population registered for package: "
169                             + mCallingService.getPackageName());
170             sendSuccess(callback);
171             return;
172         }
173         mFederatedComputeManager.cancel(
174                 mCallingService,
175                 populationName,
176                 mInjector.getExecutor(),
177                 new OutcomeReceiver<>() {
178                     @Override
179                     public void onResult(Object result) {
180                         sendSuccess(callback);
181                     }
182 
183                     @Override
184                     public void onError(Exception e) {
185                         sLogger.e(TAG + ": Error while cancelling federatedCompute", e);
186                         sendError(callback);
187                     }
188                 });
189     }
190 
sendSuccess(@onNull IFederatedComputeCallback callback)191     private static void sendSuccess(@NonNull IFederatedComputeCallback callback) {
192         try {
193             callback.onSuccess();
194         } catch (RemoteException e) {
195             sLogger.e(TAG + ": Callback error", e);
196         }
197     }
198 
sendError(@onNull IFederatedComputeCallback callback)199     private static void sendError(@NonNull IFederatedComputeCallback callback) {
200         sendError(callback, ClientConstants.STATUS_INTERNAL_ERROR);
201     }
202 
sendError(@onNull IFederatedComputeCallback callback, int errorCode)203     private static void sendError(@NonNull IFederatedComputeCallback callback, int errorCode) {
204         try {
205             callback.onFailure(errorCode);
206         } catch (RemoteException e) {
207             sLogger.e(TAG + ": Callback error", e);
208         }
209     }
210 
211     @VisibleForTesting
212     static class Injector {
getExecutor()213         ListeningExecutorService getExecutor() {
214             return OnDevicePersonalizationExecutors.getBackgroundExecutor();
215         }
216 
getFederatedComputeManager(Context context)217         FederatedComputeManager getFederatedComputeManager(Context context) {
218             return context.getSystemService(FederatedComputeManager.class);
219         }
220 
getEventsDao(Context context)221         EventsDao getEventsDao(Context context) {
222             return EventsDao.getInstance(context);
223         }
224     }
225 }
226