• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.appsindexer;
18 
19 import android.annotation.BinderThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.appsearch.AppSearchEnvironment;
23 import android.app.appsearch.AppSearchEnvironmentFactory;
24 import android.app.appsearch.exceptions.AppSearchException;
25 import android.app.appsearch.util.LogUtil;
26 import android.content.Context;
27 import android.os.CancellationSignal;
28 import android.os.UserHandle;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 import android.util.Slog;
32 
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.server.LocalManagerRegistry;
36 import com.android.server.SystemService;
37 import com.android.server.appsearch.indexer.IndexerLocalService;
38 
39 import java.io.File;
40 import java.io.PrintWriter;
41 import java.util.Map;
42 import java.util.Objects;
43 
44 /**
45  * Manages the per device-user AppOpenEventIndexer instance to index apps into AppSearch.
46  *
47  * <p>Unlike apps indexer and contacts indexer, this does not hook into package update, phone
48  * unlock/stop, etc. It only runs when the maintenance job runs.
49  *
50  * <p>This class is thread-safe.
51  *
52  * @hide
53  */
54 public final class AppOpenEventIndexerManagerService extends SystemService {
55     private static final String TAG = "AppSearchAppOpenEventIn";
56     private final Context mContext;
57     @VisibleForTesting final LocalService mLocalService;
58     @VisibleForTesting @Nullable final Runnable mCallback;
59 
60     private final AppOpenEventIndexerConfig mAppOpenEventIndexerConfig;
61 
62     // Map of AppOpenEventIndexerUserInstances indexed by the UserHandle
63     @GuardedBy("mAppOpenEventIndexersLocked")
64     private final Map<UserHandle, AppOpenEventIndexerUserInstance> mAppOpenEventIndexersLocked =
65             new ArrayMap<>();
66 
67     /** Constructs a {@link AppOpenEventIndexerManagerService}. */
AppOpenEventIndexerManagerService( @onNull Context context, @NonNull AppOpenEventIndexerConfig appOpenEventIndexerConfig)68     public AppOpenEventIndexerManagerService(
69             @NonNull Context context,
70             @NonNull AppOpenEventIndexerConfig appOpenEventIndexerConfig) {
71         this(context, appOpenEventIndexerConfig, /* callback= */ null);
72     }
73 
74     /**
75      * Constructs a {@link AppOpenEventIndexerManagerService} for testing with a callback for
76      * synchronization.
77      */
78     @VisibleForTesting
AppOpenEventIndexerManagerService( @onNull Context context, @NonNull AppOpenEventIndexerConfig appOpenEventIndexerConfig, @Nullable Runnable callback)79     public AppOpenEventIndexerManagerService(
80             @NonNull Context context,
81             @NonNull AppOpenEventIndexerConfig appOpenEventIndexerConfig,
82             @Nullable Runnable callback) {
83         super(context);
84         mContext = Objects.requireNonNull(context);
85         mAppOpenEventIndexerConfig = Objects.requireNonNull(appOpenEventIndexerConfig);
86         mCallback = callback;
87         mLocalService = new LocalService();
88     }
89 
90     @Override
onStart()91     public void onStart() {
92         LocalManagerRegistry.addManager(LocalService.class, mLocalService);
93     }
94 
95     /** Schedules the periodic update job for all users we have an instance for. */
96     @Override
onUserUnlocking(@onNull TargetUser user)97     public void onUserUnlocking(@NonNull TargetUser user) {
98         synchronized (mAppOpenEventIndexersLocked) {
99             try {
100                 AppOpenEventIndexerUserInstance instance =
101                         getOrCreateUserInstance(user.getUserHandle());
102                 if (instance != null) {
103                     instance.schedulePeriodicUpdate();
104                 }
105             } catch (RuntimeException e) {
106                 Slog.wtf(TAG, "AppOpenEventIndexerManagerService.onUserUnlocking() failed", e);
107             }
108         }
109     }
110 
111     /** Handles user stopping by shutting down the instance for the user. */
112     @Override
onUserStopping(@onNull TargetUser user)113     public void onUserStopping(@NonNull TargetUser user) {
114         try {
115             Objects.requireNonNull(user);
116             UserHandle userHandle = user.getUserHandle();
117             synchronized (mAppOpenEventIndexersLocked) {
118                 AppOpenEventIndexerUserInstance instance =
119                         mAppOpenEventIndexersLocked.get(userHandle);
120                 if (instance != null) {
121                     mAppOpenEventIndexersLocked.remove(userHandle);
122                     try {
123                         instance.shutdown();
124                     } catch (InterruptedException e) {
125                         Log.w(
126                                 TAG,
127                                 "Failed to shutdown app open event indexer for " + userHandle,
128                                 e);
129                     }
130                 }
131             }
132         } catch (RuntimeException e) {
133             Slog.wtf(TAG, "AppOpenEventIndexerManagerService.onUserStopping() failed ", e);
134         }
135     }
136 
137     /** Dumps AppOpenEventIndexer internal state for the user. */
138     @BinderThread
dumpAppOpenEventIndexerForUser( @onNull UserHandle userHandle, @NonNull PrintWriter pw)139     public void dumpAppOpenEventIndexerForUser(
140             @NonNull UserHandle userHandle, @NonNull PrintWriter pw) {
141         try {
142             Objects.requireNonNull(userHandle);
143             Objects.requireNonNull(pw);
144             synchronized (mAppOpenEventIndexersLocked) {
145                 AppOpenEventIndexerUserInstance instance =
146                         mAppOpenEventIndexersLocked.get(userHandle);
147                 if (instance != null) {
148                     instance.dump(pw);
149                 } else {
150                     pw.println("AppOpenEventIndexerUserInstance is not created for " + userHandle);
151                 }
152             }
153         } catch (RuntimeException e) {
154             Slog.wtf(
155                     TAG,
156                     "AppOpenEventIndexerManagerService.dumpAppOpenEventIndexerForUser() failed ",
157                     e);
158         }
159     }
160 
161     /** Retrieves or creates the {@link AppOpenEventIndexerUserInstance} for the specified user. */
getOrCreateUserInstance( @onNull UserHandle userHandle)162     private AppOpenEventIndexerUserInstance getOrCreateUserInstance(
163             @NonNull UserHandle userHandle) {
164         synchronized (mAppOpenEventIndexersLocked) {
165             Objects.requireNonNull(userHandle);
166             AppOpenEventIndexerUserInstance instance = mAppOpenEventIndexersLocked.get(userHandle);
167 
168             if (instance == null) {
169                 if (LogUtil.INFO) {
170                     Log.i(TAG, "Creating AppOpenEventIndexerUserInstance for " + userHandle);
171                 }
172                 try {
173                     AppSearchEnvironment appSearchEnvironment =
174                             AppSearchEnvironmentFactory.getEnvironmentInstance();
175                     Context userContext =
176                             appSearchEnvironment.createContextAsUser(mContext, userHandle);
177                     File appSearchDir =
178                             appSearchEnvironment.getAppSearchDir(userContext, userHandle);
179                     File appOpenEventDir = new File(appSearchDir, "app-open-events");
180                     instance =
181                             AppOpenEventIndexerUserInstance.createInstance(
182                                     userContext, appOpenEventDir, mAppOpenEventIndexerConfig);
183                     mAppOpenEventIndexersLocked.put(userHandle, instance);
184                 } catch (AppSearchException e) {
185                     Log.e(
186                             TAG,
187                             "Error while creating AppOpenEventIndexerUserInstance for "
188                                     + userHandle,
189                             e);
190                 }
191             }
192             return instance;
193         }
194     }
195 
196     class LocalService implements IndexerLocalService {
197         /** Runs an update for a user. */
198         @Override
doUpdateForUser( @onNull UserHandle userHandle, @Nullable CancellationSignal unused)199         public void doUpdateForUser(
200                 @NonNull UserHandle userHandle, @Nullable CancellationSignal unused) {
201 
202             Objects.requireNonNull(userHandle);
203             try {
204                 synchronized (mAppOpenEventIndexersLocked) {
205                     // Jobs are created onUserUnlocking, so unless the user is removed, this should
206                     // be non-null.
207                     AppOpenEventIndexerUserInstance instance =
208                             mAppOpenEventIndexersLocked.get(userHandle);
209                     if (instance != null) {
210                         if (mCallback != null) {
211                             instance.updateAsync(mCallback);
212                         } else {
213                             instance.updateAsync();
214                         }
215                     }
216                 }
217             } catch (RuntimeException e) {
218                 Slog.wtf(TAG, "AppOpenEventIndexerManagerService.doUpdateForUser() failed ", e);
219             }
220         }
221     }
222 }
223