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.appsearch; 18 19 import static com.android.server.appsearch.indexer.IndexerMaintenanceConfig.APPS_INDEXER; 20 import static com.android.server.appsearch.indexer.IndexerMaintenanceConfig.APP_OPEN_EVENT_INDEXER; 21 import static com.android.server.appsearch.indexer.IndexerMaintenanceConfig.CONTACTS_INDEXER; 22 23 import android.annotation.BinderThread; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.app.appsearch.util.ExceptionUtil; 27 import android.app.appsearch.util.LogUtil; 28 import android.content.Context; 29 import android.os.UserHandle; 30 import android.util.Log; 31 32 import com.android.appsearch.flags.Flags; 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.server.SystemService; 35 import com.android.server.appsearch.appsindexer.AppOpenEventIndexerConfig; 36 import com.android.server.appsearch.appsindexer.AppOpenEventIndexerManagerService; 37 import com.android.server.appsearch.appsindexer.AppsIndexerConfig; 38 import com.android.server.appsearch.appsindexer.AppsIndexerManagerService; 39 import com.android.server.appsearch.appsindexer.FrameworkAppOpenEventIndexerConfig; 40 import com.android.server.appsearch.appsindexer.FrameworkAppsIndexerConfig; 41 import com.android.server.appsearch.contactsindexer.ContactsIndexerConfig; 42 import com.android.server.appsearch.contactsindexer.ContactsIndexerManagerService; 43 import com.android.server.appsearch.contactsindexer.FrameworkContactsIndexerConfig; 44 import com.android.server.appsearch.indexer.IndexerMaintenanceService; 45 46 import java.io.PrintWriter; 47 import java.util.Objects; 48 49 /** This class encapsulate the lifecycle methods of AppSearch module. */ 50 public class AppSearchModule { 51 private static final String TAG = "AppSearchModule"; 52 53 /** Lifecycle definition for AppSearch module. */ 54 public static class Lifecycle extends SystemService { 55 private AppSearchManagerService mAppSearchManagerService; 56 @VisibleForTesting @Nullable ContactsIndexerManagerService mContactsIndexerManagerService; 57 58 @VisibleForTesting @Nullable AppsIndexerManagerService mAppsIndexerManagerService; 59 60 @VisibleForTesting @Nullable 61 AppOpenEventIndexerManagerService mAppOpenEventIndexerManagerService; 62 Lifecycle(Context context)63 public Lifecycle(Context context) { 64 super(context); 65 } 66 67 /** Added primarily for testing purposes. */ 68 @VisibleForTesting 69 @NonNull createAppSearchManagerService( @onNull Context context, @NonNull AppSearchModule.Lifecycle lifecycle)70 AppSearchManagerService createAppSearchManagerService( 71 @NonNull Context context, @NonNull AppSearchModule.Lifecycle lifecycle) { 72 Objects.requireNonNull(context); 73 Objects.requireNonNull(lifecycle); 74 return new AppSearchManagerService(context, lifecycle); 75 } 76 77 /** Added primarily for testing purposes. */ 78 @VisibleForTesting 79 @NonNull createAppsIndexerManagerService( @onNull Context context, @NonNull AppsIndexerConfig config)80 AppsIndexerManagerService createAppsIndexerManagerService( 81 @NonNull Context context, @NonNull AppsIndexerConfig config) { 82 Objects.requireNonNull(context); 83 Objects.requireNonNull(config); 84 return new AppsIndexerManagerService(context, config); 85 } 86 87 /** Added primarily for testing purposes. */ 88 @VisibleForTesting 89 @NonNull createContactsIndexerManagerService( @onNull Context context, @NonNull ContactsIndexerConfig config)90 ContactsIndexerManagerService createContactsIndexerManagerService( 91 @NonNull Context context, @NonNull ContactsIndexerConfig config) { 92 Objects.requireNonNull(context); 93 Objects.requireNonNull(config); 94 return new ContactsIndexerManagerService(context, config); 95 } 96 97 /** Added primarily for testing purposes. */ 98 @VisibleForTesting 99 @NonNull createAppOpenEventIndexerManagerService( @onNull Context context, @NonNull AppOpenEventIndexerConfig config)100 AppOpenEventIndexerManagerService createAppOpenEventIndexerManagerService( 101 @NonNull Context context, @NonNull AppOpenEventIndexerConfig config) { 102 Objects.requireNonNull(context); 103 Objects.requireNonNull(config); 104 return new AppOpenEventIndexerManagerService(context, config); 105 } 106 107 @Override onStart()108 public void onStart() { 109 mAppSearchManagerService = 110 createAppSearchManagerService(getContext(), /* lifecycle= */ this); 111 112 try { 113 mAppSearchManagerService.onStart(); 114 } catch (RuntimeException e) { 115 Log.e(TAG, "Failed to start AppSearch service", e); 116 // If AppSearch service fails to start, skip starting ContactsIndexer service 117 // since it indexes CP2 contacts into AppSearch builtin:Person corpus 118 ExceptionUtil.handleException(e); 119 return; 120 } 121 122 // It is safe to check DeviceConfig here, since SettingsProvider, which DeviceConfig 123 // uses, starts before AppSearch. 124 ContactsIndexerConfig contactsIndexerConfig = new FrameworkContactsIndexerConfig(); 125 if (contactsIndexerConfig.isContactsIndexerEnabled()) { 126 127 mContactsIndexerManagerService = 128 createContactsIndexerManagerService(getContext(), contactsIndexerConfig); 129 try { 130 mContactsIndexerManagerService.onStart(); 131 } catch (Throwable t) { 132 Log.e(TAG, "Failed to start ContactsIndexer service", t); 133 // Release the Contacts Indexer instance as it won't be started until the next 134 // system_server restart on a device reboot. 135 mContactsIndexerManagerService = null; 136 } 137 } else if (LogUtil.INFO) { 138 Log.i(TAG, "ContactsIndexer service is disabled."); 139 } 140 141 AppsIndexerConfig appsIndexerConfig = new FrameworkAppsIndexerConfig(); 142 // Flags.appsIndexerEnabled will be rolled out through gantry, and this check will be 143 // removed once it is fully rolled out. appsIndexerConfig.isAppsIndexerEnabled checks 144 // DeviceConfig, so we can keep this check here in case we need to turn off apps 145 // indexer. 146 if (Flags.appsIndexerEnabled() && appsIndexerConfig.isAppsIndexerEnabled()) { 147 mAppsIndexerManagerService = 148 createAppsIndexerManagerService(getContext(), appsIndexerConfig); 149 try { 150 mAppsIndexerManagerService.onStart(); 151 } catch (Throwable t) { 152 Log.e(TAG, "Failed to start AppsIndexer service", t); 153 mAppsIndexerManagerService = null; 154 } 155 } else if (LogUtil.INFO) { 156 Log.i(TAG, "AppsIndexer service is disabled."); 157 } 158 159 AppOpenEventIndexerConfig appOpenEventIndexerConfig = 160 new FrameworkAppOpenEventIndexerConfig(); 161 // Flags.appOpenEventIndexerEnabled will be rolled out through gantry, and this check 162 // will be removed once it is fully rolled out. 163 // appOpenEventIndexerConfig.isAppOpenEventIndexerEnabled checks DeviceConfig, so we can 164 // keep this check here in case we need to turn off app open event indexer. 165 if (Flags.appOpenEventIndexerEnabled() 166 && appOpenEventIndexerConfig.isAppOpenEventIndexerEnabled()) { 167 mAppOpenEventIndexerManagerService = 168 createAppOpenEventIndexerManagerService( 169 getContext(), appOpenEventIndexerConfig); 170 try { 171 mAppOpenEventIndexerManagerService.onStart(); 172 } catch (Throwable t) { 173 Log.e(TAG, "Failed to start app open event indexer service", t); 174 mAppOpenEventIndexerManagerService = null; 175 } 176 } else if (LogUtil.INFO) { 177 Log.i(TAG, "AppOpenEventIndexer service is disabled."); 178 } 179 } 180 181 /** Dumps ContactsIndexer internal state for the user. */ 182 @BinderThread dumpContactsIndexerForUser( @onNull UserHandle userHandle, @NonNull PrintWriter pw, boolean verbose)183 void dumpContactsIndexerForUser( 184 @NonNull UserHandle userHandle, @NonNull PrintWriter pw, boolean verbose) { 185 if (mContactsIndexerManagerService != null) { 186 mContactsIndexerManagerService.dumpContactsIndexerForUser(userHandle, pw, verbose); 187 } else { 188 pw.println("No dumpsys for ContactsIndexer as it is disabled."); 189 } 190 } 191 192 @BinderThread dumpAppsIndexerForUser(@onNull UserHandle userHandle, @NonNull PrintWriter pw)193 void dumpAppsIndexerForUser(@NonNull UserHandle userHandle, @NonNull PrintWriter pw) { 194 if (mAppsIndexerManagerService != null) { 195 mAppsIndexerManagerService.dumpAppsIndexerForUser(userHandle, pw); 196 } else { 197 pw.println("No dumpsys for AppsIndexer as it is disabled"); 198 } 199 } 200 201 @BinderThread dumpAppOpenEventIndexerForUser( @onNull UserHandle userHandle, @NonNull PrintWriter pw)202 void dumpAppOpenEventIndexerForUser( 203 @NonNull UserHandle userHandle, @NonNull PrintWriter pw) { 204 if (mAppOpenEventIndexerManagerService != null) { 205 mAppOpenEventIndexerManagerService.dumpAppOpenEventIndexerForUser(userHandle, pw); 206 } else { 207 pw.println("No dumpsys for AppOpenEventIndexer as it is disabled"); 208 } 209 } 210 211 @Override onBootPhase(int phase)212 public void onBootPhase(int phase) { 213 mAppSearchManagerService.onBootPhase(phase); 214 } 215 216 @Override onUserUnlocking(@onNull TargetUser user)217 public void onUserUnlocking(@NonNull TargetUser user) { 218 mAppSearchManagerService.onUserUnlocking(user); 219 if (mContactsIndexerManagerService == null) { 220 IndexerMaintenanceService.cancelUpdateJobIfScheduled( 221 getContext(), user.getUserHandle(), CONTACTS_INDEXER); 222 } else { 223 mContactsIndexerManagerService.onUserUnlocking(user); 224 } 225 226 if (mAppsIndexerManagerService == null) { 227 IndexerMaintenanceService.cancelUpdateJobIfScheduled( 228 getContext(), user.getUserHandle(), APPS_INDEXER); 229 } else { 230 mAppsIndexerManagerService.onUserUnlocking(user); 231 } 232 233 if (mAppOpenEventIndexerManagerService == null) { 234 IndexerMaintenanceService.cancelUpdateJobIfScheduled( 235 getContext(), user.getUserHandle(), APP_OPEN_EVENT_INDEXER); 236 } else { 237 // App Open Event Indexer only schedules a periodic update job on user unlock and 238 // does not run an update 239 mAppOpenEventIndexerManagerService.onUserUnlocking(user); 240 } 241 } 242 243 @Override onUserStopping(@onNull TargetUser user)244 public void onUserStopping(@NonNull TargetUser user) { 245 mAppSearchManagerService.onUserStopping(user); 246 if (mContactsIndexerManagerService != null) { 247 mContactsIndexerManagerService.onUserStopping(user); 248 } 249 if (mAppsIndexerManagerService != null) { 250 mAppsIndexerManagerService.onUserStopping(user); 251 } 252 if (mAppOpenEventIndexerManagerService != null) { 253 mAppOpenEventIndexerManagerService.onUserStopping(user); 254 } 255 } 256 } 257 } 258