blob: 8c953d12fd9826652ebec4437c4860b74337a4a7 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.appsearch;
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import java.io.File;
/**
* Manages the lifecycle of instances of {@link AppSearchImpl}.
*
* <p>These instances are managed per unique device-user.
*/
public final class ImplInstanceManager {
private static final String APP_SEARCH_DIR = "appSearch";
private static ImplInstanceManager sImplInstanceManager;
@GuardedBy("mInstancesLocked")
private final SparseArray<AppSearchImpl> mInstancesLocked = new SparseArray<>();
private final String mGlobalQuerierPackage;
private ImplInstanceManager(@NonNull String globalQuerierPackage) {
mGlobalQuerierPackage = globalQuerierPackage;
}
/**
* Gets an instance of ImplInstanceManager to be used.
*
* <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
* existing instance will be returned.
*/
@NonNull
public static ImplInstanceManager getInstance(@NonNull Context context) {
if (sImplInstanceManager == null) {
synchronized (ImplInstanceManager.class) {
if (sImplInstanceManager == null) {
sImplInstanceManager =
new ImplInstanceManager(
getGlobalAppSearchDataQuerierPackageName(context));
}
}
}
return sImplInstanceManager;
}
/**
* Gets an instance of AppSearchImpl for the given user, or creates one if none exists.
*
* <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
* one will be created.
*
* @param context The context
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link AppSearchImpl} for this user
*/
@NonNull
public AppSearchImpl getOrCreateAppSearchImpl(
@NonNull Context context, @UserIdInt int userId) throws AppSearchException {
synchronized (mInstancesLocked) {
AppSearchImpl instance = mInstancesLocked.get(userId);
if (instance == null) {
instance = createImpl(context, userId);
mInstancesLocked.put(userId, instance);
}
return instance;
}
}
/**
* Gets an instance of AppSearchImpl for the given user.
*
* <p>This method should only be called by an initialized SearchSession, which has been already
* created the AppSearchImpl instance for the given user.
*
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link AppSearchImpl} for this user
* @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user.
*/
@NonNull
public AppSearchImpl getAppSearchImpl(@UserIdInt int userId) {
synchronized (mInstancesLocked) {
AppSearchImpl instance = mInstancesLocked.get(userId);
if (instance == null) {
// Impossible scenario, user cannot call an uninitialized SearchSession,
// getInstance should always find the instance for the given user and never try to
// create an instance for this user again.
throw new IllegalStateException(
"AppSearchImpl has never been created for this user: " + userId);
}
return instance;
}
}
private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
File appSearchDir = getAppSearchDir(context, userId);
return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
}
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
// See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs
//TODO(b/177685938):Switch from getDataUserCePackageDirectory to getDataSystemCeDirectory
File userCeDir =
Environment.getDataUserCePackageDirectory(
StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName());
return new File(userCeDir, APP_SEARCH_DIR);
}
/**
* Returns the global querier package if it's a system package. Otherwise, empty string.
*
* @param context Context of the system service.
*/
@NonNull
private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) {
String globalAppSearchDataQuerierPackage =
context.getString(R.string.config_globalAppSearchDataQuerierPackage);
try {
if (context.getPackageManager()
.getPackageInfoAsUser(
globalAppSearchDataQuerierPackage,
MATCH_FACTORY_ONLY,
UserHandle.USER_SYSTEM)
== null) {
return "";
}
} catch (PackageManager.NameNotFoundException e) {
return "";
}
return globalAppSearchDataQuerierPackage;
}
}