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 = new App(this, pm, packageName); SwapService.putAppInCache(packageName, app); } catch (CertificateEncodingException | IOException | PackageManager.NameNotFoundException e) { e.printStackTrace(); } } }