purge CacheSwapAppsService in favor of InstalledAppProvider

The most expensive part of this whole process is calculating the hash of the
whole APK.  InstalledAppProvider already caches that, and the rest is OK to
query.  If any particular part of the query is expensive, it could also be
moved to InstalledAppProviderService.
This commit is contained in:
Hans-Christoph Steiner 2019-05-14 23:47:24 +02:00
parent 85410504da
commit ea3b47f705
5 changed files with 8 additions and 112 deletions

View File

@ -77,9 +77,6 @@
android:exported="false"/>
<service android:name=".localrepo.SwapService"/>
<service
android:name=".localrepo.CacheSwapAppsService"
android:exported="false"/>
<service
android:name=".localrepo.LocalRepoService"
android:exported="false"/>

View File

@ -1,85 +0,0 @@
package org.fdroid.fdroid.localrepo;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.App;
import java.io.File;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
/**
* An {@link IntentService} subclass for generating cached info about the installed APKs
* which are available for swapping. It does not cache system apps, since those are
* rarely swapped. This is meant to start running when {@link SwapService} starts.
* <p>
* This could probably be replaced by {@link org.fdroid.fdroid.data.InstalledAppProvider}
* if that contained all of the info to generate complete {@link App} and
* {@link org.fdroid.fdroid.data.Apk} instances.
*/
public class CacheSwapAppsService extends IntentService {
private static final String TAG = "CacheSwapAppsService";
private static final String ACTION_PARSE_APP = "org.fdroid.fdroid.localrepo.action.PARSE_APP";
public CacheSwapAppsService() {
super("CacheSwapAppsService");
}
/**
* Parse the locally installed APK for {@code packageName} and save its XML
* to the APK XML cache.
*/
private static void parseApp(Context context, String packageName) {
Intent intent = new Intent();
intent.setData(Utils.getPackageUri(packageName));
intent.setClass(context, CacheSwapAppsService.class);
intent.setAction(ACTION_PARSE_APP);
context.startService(intent);
}
/**
* Parse all of the locally installed APKs into a memory cache, starting
* with the currently selected apps. APKs that are already parsed in the
* {@code index.jar} file will be read from that file.
*/
public static void startCaching(Context context) {
File indexJarFile = LocalRepoManager.get(context).getIndexJar();
PackageManager pm = context.getPackageManager();
for (ApplicationInfo applicationInfo : pm.getInstalledApplications(0)) {
if (applicationInfo.publicSourceDir.startsWith(FDroidApp.SYSTEM_DIR_NAME)) {
continue;
}
if (!indexJarFile.exists()
|| FileUtils.isFileNewer(new File(applicationInfo.sourceDir), indexJarFile)) {
parseApp(context, applicationInfo.packageName);
}
}
}
@Override
protected void onHandleIntent(Intent intent) {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST);
if (intent == null || !ACTION_PARSE_APP.equals(intent.getAction())) {
Utils.debugLog(TAG, "received bad Intent: " + intent);
return;
}
try {
PackageManager pm = getPackageManager();
String packageName = intent.getData().getSchemeSpecificPart();
App app = App.getInstance(this, pm, packageName);
if (app != null) {
SwapService.putAppInCache(packageName, app);
}
} catch (CertificateEncodingException | IOException | PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}

View File

@ -276,12 +276,10 @@ public final class LocalRepoManager {
}
public void addApp(Context context, String packageName) {
App app;
App app = null;
try {
app = SwapService.getAppFromCache(packageName);
if (app == null) {
app = App.getInstance(context.getApplicationContext(), pm, packageName);
}
InstalledApp installedApp = InstalledAppProvider.Helper.findByPackageName(context, packageName);
app = App.getInstance(context, pm, installedApp, packageName);
if (app == null || !app.isValid()) {
return;
}

View File

@ -27,7 +27,6 @@ import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema;
@ -53,7 +52,6 @@ import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* Central service which manages all of the different moving parts of swap which are required
@ -74,11 +72,6 @@ public class SwapService extends Service {
@NonNull
private final Set<String> appsToSwap = new HashSet<>();
/**
* A cache of parsed APKs from the file system.
*/
private static final ConcurrentHashMap<String, App> INSTALLED_APPS = new ConcurrentHashMap<>();
private static SharedPreferences swapPreferences;
private static BluetoothAdapter bluetoothAdapter;
private static WifiManager wifiManager;
@ -88,14 +81,6 @@ public class SwapService extends Service {
context.stopService(intent);
}
static App getAppFromCache(String packageName) {
return INSTALLED_APPS.get(packageName);
}
static void putAppInCache(String packageName, @NonNull App app) {
INSTALLED_APPS.put(packageName, app);
}
// ==========================================================
// Search for peers to swap
// ==========================================================
@ -447,8 +432,6 @@ public class SwapService extends Service {
deleteAllSwapRepos();
CacheSwapAppsService.startCaching(this);
swapPreferences = getSharedPreferences(SHARED_PREFERENCES, Context.MODE_PRIVATE);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

View File

@ -385,13 +385,16 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
* exists.
*/
@Nullable
public static App getInstance(Context context, PackageManager pm, String packageName)
public static App getInstance(Context context, PackageManager pm, InstalledApp installedApp, String packageName)
throws CertificateEncodingException, IOException, PackageManager.NameNotFoundException {
App app = new App();
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
SanitizedFile apkFile = SanitizedFile.knownSanitized(packageInfo.applicationInfo.publicSourceDir);
app.installedApk = new Apk();
if (apkFile.canRead()) {
if (installedApp != null) {
app.installedApk.hashType = installedApp.getHashType();
app.installedApk.hash = installedApp.getHash();
} else if (apkFile.canRead()) {
String hashType = "sha256";
String hash = Utils.getBinaryHash(apkFile, hashType);
if (TextUtils.isEmpty(hash)) {