• 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.server.healthconnect;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.health.connect.ratelimiter.RateLimiter;
22 import android.os.Process;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.util.Slog;
26 
27 import com.android.healthfitness.flags.Flags;
28 import com.android.server.SystemService;
29 import com.android.server.healthconnect.exportimport.ExportImportJobs;
30 import com.android.server.healthconnect.injector.HealthConnectInjector;
31 import com.android.server.healthconnect.injector.HealthConnectInjectorImpl;
32 import com.android.server.healthconnect.migration.MigratorPackageChangesReceiver;
33 import com.android.server.healthconnect.storage.HealthConnectContext;
34 
35 import java.util.Objects;
36 
37 /**
38  * HealthConnect system service scaffold.
39  *
40  * @hide
41  */
42 public class HealthConnectManagerService extends SystemService {
43     private static final String TAG = "HealthConnectManagerService";
44     private final Context mContext;
45     private final HealthConnectServiceImpl mHealthConnectService;
46     private final UserManager mUserManager;
47     private final HealthConnectInjector mHealthConnectInjector;
48     private final RateLimiter mRateLimiter;
49 
50     private UserHandle mCurrentForegroundUser;
51 
HealthConnectManagerService(Context context)52     public HealthConnectManagerService(Context context) {
53         super(context);
54         mRateLimiter = new RateLimiter();
55         mContext = context;
56         mCurrentForegroundUser = context.getUser();
57         mUserManager = context.getSystemService(UserManager.class);
58 
59         HealthConnectInjector.setInstance(new HealthConnectInjectorImpl(context));
60         mHealthConnectInjector = HealthConnectInjector.getInstance();
61         mHealthConnectService =
62                 new HealthConnectServiceImpl(
63                         mContext,
64                         mHealthConnectInjector.getTimeSource(),
65                         mHealthConnectInjector.getInternalHealthConnectMappings(),
66                         mHealthConnectInjector.getTransactionManager(),
67                         mHealthConnectInjector.getHealthConnectPermissionHelper(),
68                         mHealthConnectInjector.getFirstGrantTimeManager(),
69                         mHealthConnectInjector.getMigrationEntityHelper(),
70                         mHealthConnectInjector.getMigrationStateManager(),
71                         mHealthConnectInjector.getMigrationUiStateManager(),
72                         mHealthConnectInjector.getMigrationCleaner(),
73                         mHealthConnectInjector.getFitnessRecordUpsertHelper(),
74                         mHealthConnectInjector.getFitnessRecordReadHelper(),
75                         mHealthConnectInjector.getFitnessRecordDeleteHelper(),
76                         mHealthConnectInjector.getFitnessRecordAggregateHelper(),
77                         mHealthConnectInjector.getMedicalResourceHelper(),
78                         mHealthConnectInjector.getMedicalDataSourceHelper(),
79                         mHealthConnectInjector.getExportManager(),
80                         mHealthConnectInjector.getExportImportSettingsStorage(),
81                         mHealthConnectInjector.getExportImportNotificationSender(),
82                         mHealthConnectInjector.getBackupRestore(),
83                         mHealthConnectInjector.getAccessLogsHelper(),
84                         mHealthConnectInjector.getHealthDataCategoryPriorityHelper(),
85                         mHealthConnectInjector.getActivityDateHelper(),
86                         mHealthConnectInjector.getChangeLogsHelper(),
87                         mHealthConnectInjector.getChangeLogsRequestHelper(),
88                         mHealthConnectInjector.getPriorityMigrationHelper(),
89                         mHealthConnectInjector.getAppInfoHelper(),
90                         mHealthConnectInjector.getDeviceInfoHelper(),
91                         mHealthConnectInjector.getPreferenceHelper(),
92                         mHealthConnectInjector.getDatabaseHelpers(),
93                         mHealthConnectInjector.getPreferencesManager(),
94                         mHealthConnectInjector.getReadAccessLogsHelper(),
95                         mHealthConnectInjector.getAppOpsManagerLocal(),
96                         mHealthConnectInjector.getThreadScheduler(),
97                         mRateLimiter,
98                         mHealthConnectInjector.getEnvironmentDataDirectory(),
99                         mHealthConnectInjector.getExportImportLogger(),
100                         mHealthConnectInjector.getHealthFitnessStatsLog(),
101                         mHealthConnectInjector.getBackupRestoreLogger());
102     }
103 
104     @Override
onStart()105     public void onStart() {
106         mHealthConnectInjector
107                 .getPermissionPackageChangesOrchestrator()
108                 .registerBroadcastReceiver(mContext);
109         new MigratorPackageChangesReceiver(mHealthConnectInjector.getMigrationStateManager())
110                 .registerBroadcastReceiver(mContext);
111         publishBinderService(Context.HEALTHCONNECT_SERVICE, mHealthConnectService);
112     }
113 
114     /**
115      * NOTE: Don't put any code that uses DB in onUserSwitching, such code should be part of
116      * switchToSetupForUser which is only called once DB is in usable state.
117      */
118     @Override
onUserSwitching(@ullable TargetUser from, TargetUser to)119     public void onUserSwitching(@Nullable TargetUser from, TargetUser to) {
120         if (from != null && mUserManager.isUserUnlocked(from.getUserHandle())) {
121             // We need to cancel any pending timers for the foreground user before it goes into the
122             // background.
123             mHealthConnectService.cancelBackupRestoreTimeouts();
124         }
125 
126         HealthConnectThreadScheduler threadScheduler = mHealthConnectInjector.getThreadScheduler();
127         threadScheduler.shutdownThreadPools();
128         mRateLimiter.clearCache();
129         HealthConnectDailyJobs.cancelAllJobs(mContext);
130         mHealthConnectInjector.getDatabaseHelpers().clearAllCache();
131         mHealthConnectInjector.getTransactionManager().shutDownCurrentUser();
132         mHealthConnectInjector.getMigrationStateManager().shutDownCurrentUser(mContext);
133         threadScheduler.resetThreadPools();
134 
135         mCurrentForegroundUser = to.getUserHandle();
136 
137         if (mUserManager.isUserUnlocked(to.getUserHandle())) {
138             // The user is already in unlocked state, so we should proceed with our setup right now,
139             // as we won't be getting a onUserUnlocked callback
140             setupForCurrentForegroundUser();
141         }
142     }
143 
144     // NOTE: The only scenario in which onUserUnlocked's code should be triggered is if the
145     // foreground user is unlocked. If {@code user} is not a foreground user, the following
146     // code should only be triggered when the {@code user} actually gets unlocked. And in
147     // such cases onUserSwitching will be triggered for {@code user} and this code will be
148     // triggered then.
149     @Override
onUserUnlocked(TargetUser user)150     public void onUserUnlocked(TargetUser user) {
151         Objects.requireNonNull(user);
152         if (!user.getUserHandle().equals(mCurrentForegroundUser)) {
153             // Ignore unlocking requests for non-foreground users
154             return;
155         }
156 
157         setupForCurrentForegroundUser();
158     }
159 
160     @Override
isUserSupported(TargetUser user)161     public boolean isUserSupported(TargetUser user) {
162         UserManager userManager =
163                 getUserContext(mContext, user.getUserHandle()).getSystemService(UserManager.class);
164         return !(Objects.requireNonNull(userManager).isProfile());
165     }
166 
setupForCurrentForegroundUser()167     private void setupForCurrentForegroundUser() {
168         Slog.d(TAG, "setupForCurrentForegroundUser: " + mCurrentForegroundUser);
169         HealthConnectContext hcContext =
170                 HealthConnectContext.create(
171                         mContext,
172                         mCurrentForegroundUser,
173                         /* databaseDirName= */ null,
174                         mHealthConnectInjector.getEnvironmentDataDirectory());
175 
176         mHealthConnectService.setupForUser(mCurrentForegroundUser);
177         mHealthConnectInjector.getTransactionManager().setupForUser(hcContext);
178         mHealthConnectInjector.getMigrationStateManager().setupForUser(mCurrentForegroundUser);
179         mHealthConnectInjector
180                 .getMigrationBroadcastScheduler()
181                 .setupForUser(mCurrentForegroundUser);
182         mHealthConnectInjector.getMigrationUiStateManager().setupForUser(mCurrentForegroundUser);
183         mHealthConnectInjector
184                 .getPermissionPackageChangesOrchestrator()
185                 .setupForUser(mCurrentForegroundUser);
186         mHealthConnectInjector
187                 .getHealthPermissionIntentAppsTracker()
188                 .setupForUser(mCurrentForegroundUser);
189         mHealthConnectInjector.getBackupRestore().setupForUser(mCurrentForegroundUser);
190         mHealthConnectInjector.getAppInfoHelper().setupForUser(hcContext);
191         mHealthConnectInjector.getHealthDataCategoryPriorityHelper().setupForUser(hcContext);
192 
193         if (Flags.clearCachesAfterSwitchingUser()) {
194             // Clear preferences cache again after the user switching is done as there's a race
195             // condition with tasks re-populating the preferences cache between clearing the cache
196             // and TransactionManager switching user, see b/355426144.
197             mHealthConnectInjector.getPreferenceHelper().clearCache();
198         }
199 
200         HealthConnectThreadScheduler threadScheduler = mHealthConnectInjector.getThreadScheduler();
201         threadScheduler.scheduleInternalTask(
202                 () -> {
203                     try {
204                         HealthConnectDailyJobs.schedule(mContext, mCurrentForegroundUser);
205                     } catch (Exception e) {
206                         Slog.e(TAG, "Failed to schedule Health Connect daily service.", e);
207                     }
208                 });
209 
210         threadScheduler.scheduleInternalTask(
211                 () -> {
212                     try {
213                         mHealthConnectInjector
214                                 .getMigrationBroadcastScheduler()
215                                 .scheduleNewJobs(
216                                         mContext,
217                                         mHealthConnectInjector.getMigrationStateManager());
218                     } catch (Exception e) {
219                         Slog.e(TAG, "Migration broadcast schedule failed", e);
220                     }
221                 });
222 
223         threadScheduler.scheduleInternalTask(
224                 () -> {
225                     try {
226                         mHealthConnectInjector
227                                 .getMigrationStateManager()
228                                 .switchToSetupForUser(mContext);
229                     } catch (Exception e) {
230                         Slog.e(TAG, "Failed to start user unlocked state changes actions", e);
231                     }
232                 });
233         threadScheduler.scheduleInternalTask(
234                 () -> {
235                     try {
236                         mHealthConnectInjector.getPreferenceHelper().initializePreferences();
237                     } catch (Exception e) {
238                         Slog.e(TAG, "Failed to initialize preferences cache", e);
239                     }
240                 });
241 
242         threadScheduler.scheduleInternalTask(
243                 () -> {
244                     try {
245                         ExportImportJobs.schedulePeriodicJobIfNotScheduled(
246                                 mCurrentForegroundUser,
247                                 mContext,
248                                 mHealthConnectInjector.getExportImportSettingsStorage(),
249                                 mHealthConnectInjector.getExportManager());
250                     } catch (Exception e) {
251                         Slog.e(TAG, "Failed to schedule periodic export job.", e);
252                     }
253                 });
254 
255         if (Flags.stepTrackingEnabled()) {
256             threadScheduler.scheduleInternalTask(
257                     () -> {
258                         try {
259                             mHealthConnectInjector.getTrackerManager().initialize();
260                         } catch (Exception e) {
261                             Slog.e(TAG, "Failed to initialize steps tracker.", e);
262                         }
263                     });
264         }
265     }
266 
getUserContext(Context context, UserHandle user)267     private static Context getUserContext(Context context, UserHandle user) {
268         if (Process.myUserHandle().equals(user)) {
269             return context;
270         } else {
271             return context.createContextAsUser(user, 0);
272         }
273     }
274 }
275