• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.appsearch.exceptions.AppSearchException;
22 import android.content.Context;
23 import android.os.SystemClock;
24 import android.os.UserHandle;
25 import android.util.ArrayMap;
26 import android.util.Log;
27 
28 import com.android.internal.annotations.GuardedBy;
29 import com.android.server.appsearch.external.localstorage.AppSearchImpl;
30 import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
31 import com.android.server.appsearch.stats.PlatformLogger;
32 import com.android.server.appsearch.visibilitystore.VisibilityCheckerImpl;
33 
34 import java.io.File;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 
40 /**
41  * Manages the lifecycle of AppSearch classes that should only be initialized once per device-user
42  * and make up the core of the AppSearch system.
43  *
44  * @hide
45  */
46 public final class AppSearchUserInstanceManager {
47     private static final String TAG = "AppSearchUserInstanceMa";
48 
49     private static volatile AppSearchUserInstanceManager sAppSearchUserInstanceManager;
50 
51     @GuardedBy("mInstancesLocked")
52     private final Map<UserHandle, AppSearchUserInstance> mInstancesLocked = new ArrayMap<>();
53     @GuardedBy("mStorageInfoLocked")
54     private final Map<UserHandle, UserStorageInfo> mStorageInfoLocked = new ArrayMap<>();
55 
AppSearchUserInstanceManager()56     private AppSearchUserInstanceManager() {}
57 
58     /**
59      * Gets an instance of AppSearchUserInstanceManager to be used.
60      *
61      * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
62      * existing instance will be returned.
63      */
64     @NonNull
getInstance()65     public static AppSearchUserInstanceManager getInstance() {
66         if (sAppSearchUserInstanceManager == null) {
67             synchronized (AppSearchUserInstanceManager.class) {
68                 if (sAppSearchUserInstanceManager == null) {
69                     sAppSearchUserInstanceManager = new AppSearchUserInstanceManager();
70                 }
71             }
72         }
73         return sAppSearchUserInstanceManager;
74     }
75 
76     /**
77      * Gets an instance of AppSearchUserInstance for the given user, or creates one if none exists.
78      *
79      * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and
80      * one will be created.
81      *
82      * @param userContext Context of the user calling AppSearch
83      * @param userHandle The multi-user handle of the device user calling AppSearch
84      * @param config Flag manager for AppSearch
85      * @return An initialized {@link AppSearchUserInstance} for this user
86      */
87     @NonNull
getOrCreateUserInstance( @onNull Context userContext, @NonNull UserHandle userHandle, @NonNull AppSearchConfig config)88     public AppSearchUserInstance getOrCreateUserInstance(
89             @NonNull Context userContext,
90             @NonNull UserHandle userHandle,
91             @NonNull AppSearchConfig config)
92             throws AppSearchException {
93         Objects.requireNonNull(userContext);
94         Objects.requireNonNull(userHandle);
95         Objects.requireNonNull(config);
96 
97         synchronized (mInstancesLocked) {
98             AppSearchUserInstance instance = mInstancesLocked.get(userHandle);
99             if (instance == null) {
100                 instance = createUserInstance(userContext, userHandle, config);
101                 mInstancesLocked.put(userHandle, instance);
102             }
103             return instance;
104         }
105     }
106 
107     /**
108      * Closes and removes an {@link AppSearchUserInstance} for the given user.
109      *
110      * <p>All mutations applied to the underlying {@link AppSearchImpl} will be persisted to disk.
111      *
112      * @param userHandle The multi-user user handle of the user that need to be removed.
113      */
closeAndRemoveUserInstance(@onNull UserHandle userHandle)114     public void closeAndRemoveUserInstance(@NonNull UserHandle userHandle) {
115         Objects.requireNonNull(userHandle);
116         synchronized (mInstancesLocked) {
117             AppSearchUserInstance instance = mInstancesLocked.remove(userHandle);
118             if (instance != null) {
119                 instance.getAppSearchImpl().close();
120             }
121         }
122         synchronized (mStorageInfoLocked) {
123             mStorageInfoLocked.remove(userHandle);
124         }
125     }
126 
127     /**
128      * Gets an {@link AppSearchUserInstance} for the given user.
129      *
130      * <p>This method should only be called by an initialized SearchSession, which has already
131      * called {@link #getOrCreateUserInstance} before.
132      *
133      * @param userHandle The multi-user handle of the device user calling AppSearch
134      * @return An initialized {@link AppSearchUserInstance} for this user
135      * @throws IllegalStateException if {@link AppSearchUserInstance} haven't created for the given
136      *                               user.
137      */
138     @NonNull
getUserInstance(@onNull UserHandle userHandle)139     public AppSearchUserInstance getUserInstance(@NonNull UserHandle userHandle) {
140         Objects.requireNonNull(userHandle);
141         synchronized (mInstancesLocked) {
142             AppSearchUserInstance instance = mInstancesLocked.get(userHandle);
143             if (instance == null) {
144                 // Impossible scenario, user cannot call an uninitialized SearchSession,
145                 // getInstance should always find the instance for the given user and never try to
146                 // create an instance for this user again.
147                 throw new IllegalStateException(
148                         "AppSearchUserInstance has never been created for: " + userHandle);
149             }
150             return instance;
151         }
152     }
153 
154     /**
155      * Returns the initialized {@link AppSearchUserInstance} for the given user, or {@code null} if
156      * no such instance exists.
157      *
158      * @param userHandle The multi-user handle of the device user calling AppSearch
159      */
160     @Nullable
getUserInstanceOrNull(@onNull UserHandle userHandle)161     public AppSearchUserInstance getUserInstanceOrNull(@NonNull UserHandle userHandle) {
162         Objects.requireNonNull(userHandle);
163         synchronized (mInstancesLocked) {
164             return mInstancesLocked.get(userHandle);
165         }
166     }
167 
168     /**
169      * Gets an {@link UserStorageInfo} for the given user.
170      *
171      * @param userHandle The multi-user handle of the device user
172      * @return An initialized {@link UserStorageInfo} for this user
173      */
174     @NonNull
getOrCreateUserStorageInfoInstance(@onNull UserHandle userHandle)175     public UserStorageInfo getOrCreateUserStorageInfoInstance(@NonNull UserHandle userHandle) {
176         Objects.requireNonNull(userHandle);
177         synchronized (mStorageInfoLocked) {
178             UserStorageInfo userStorageInfo = mStorageInfoLocked.get(userHandle);
179             if (userStorageInfo == null) {
180                 userStorageInfo = new UserStorageInfo(AppSearchModule.getAppSearchDir(userHandle));
181                 mStorageInfoLocked.put(userHandle, userStorageInfo);
182             }
183             return userStorageInfo;
184         }
185     }
186 
187     /**
188      * Returns the list of all {@link UserHandle}s.
189      *
190      * <p>It can return an empty list if there is no {@link AppSearchUserInstance} created yet.
191      */
192     @NonNull
getAllUserHandles()193     public List<UserHandle> getAllUserHandles() {
194         synchronized (mInstancesLocked) {
195             return new ArrayList<>(mInstancesLocked.keySet());
196         }
197     }
198 
199     @NonNull
createUserInstance( @onNull Context userContext, @NonNull UserHandle userHandle, @NonNull AppSearchConfig config)200     private AppSearchUserInstance createUserInstance(
201             @NonNull Context userContext,
202             @NonNull UserHandle userHandle,
203             @NonNull AppSearchConfig config)
204             throws AppSearchException {
205         long totalLatencyStartMillis = SystemClock.elapsedRealtime();
206         InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
207 
208         // Initialize the classes that make up AppSearchUserInstance
209         PlatformLogger logger = new PlatformLogger(userContext, config);
210 
211         File appSearchDir = AppSearchModule.getAppSearchDir(userHandle);
212         File icingDir = new File(appSearchDir, "icing");
213         Log.i(TAG, "Creating new AppSearch instance at: " + icingDir);
214         VisibilityCheckerImpl visibilityCheckerImpl = new VisibilityCheckerImpl(userContext);
215         AppSearchImpl appSearchImpl = AppSearchImpl.create(
216                 icingDir,
217                 new FrameworkLimitConfig(config),
218                 initStatsBuilder,
219                 new FrameworkOptimizeStrategy(config),
220                 visibilityCheckerImpl);
221 
222         // Update storage info file
223         UserStorageInfo userStorageInfo = getOrCreateUserStorageInfoInstance(userHandle);
224         userStorageInfo.updateStorageInfoFile(appSearchImpl);
225 
226         initStatsBuilder
227                 .setTotalLatencyMillis(
228                         (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
229         logger.logStats(initStatsBuilder.build());
230 
231         return new AppSearchUserInstance(logger, appSearchImpl, visibilityCheckerImpl);
232     }
233 }
234