handle installing OTA files separately from generic .zip files
It is valid to include .zip files in a repo, but only OTA ZIP files should be installed into the OTA dir.
This commit is contained in:
parent
5a0092d42e
commit
4bb158ef77
@ -17,14 +17,18 @@ import androidx.annotation.Nullable;
|
||||
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.fdroid.fdroid.BuildConfig;
|
||||
import org.fdroid.fdroid.Utils;
|
||||
import org.fdroid.fdroid.data.Schema.ApkTable.Cols;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Represents a single package of an application. This represents one particular
|
||||
@ -551,10 +555,12 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the install path for a "non-apk" media file
|
||||
* Defaults to {@link android.os.Environment#DIRECTORY_DOWNLOADS}
|
||||
* Get the install path for a "non-apk" media file, with special cases for
|
||||
* files that can be usefully installed without PrivilegedExtension.
|
||||
* Defaults to {@link android.os.Environment#DIRECTORY_DOWNLOADS}.
|
||||
*
|
||||
* @return the install path for this {@link Apk}
|
||||
* @link <a href="https://source.android.com/devices/tech/ota/nonab/inside_packages">Inside OTA Packages</a>
|
||||
*/
|
||||
|
||||
public File getMediaInstallPath(Context context) {
|
||||
@ -579,11 +585,22 @@ public class Apk extends ValueObject implements Comparable<Apk>, Parcelable {
|
||||
} else if ("video".equals(topLevelType)) {
|
||||
path = Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_MOVIES);
|
||||
// TODO support OsmAnd map files, other map apps?
|
||||
//} else if (mimeTypeMap.hasExtension("map")) { // OsmAnd map files
|
||||
//} else if (this.apkName.matches(".*.ota_[0-9]*.zip")) { // Over-The-Air update ZIP files
|
||||
} else if (this.apkName.endsWith(".zip")) { // Over-The-Air update ZIP files
|
||||
path = new File(context.getApplicationInfo().dataDir + "/ota");
|
||||
} else if ("zip".equals(fileExtension)) {
|
||||
try {
|
||||
File cachedFile = ApkCache.getApkDownloadPath(context, this.getCanonicalUrl());
|
||||
ZipFile zipFile = new ZipFile(cachedFile);
|
||||
if (zipFile.getEntry("META-INF/com/google/android/update-binary") != null) {
|
||||
// Over-The-Air update ZIP files
|
||||
return new File(context.getApplicationInfo().dataDir + "/ota");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// this should happen when running isMediaInstalled() and the file isn't installed
|
||||
// other cases are probably bugs
|
||||
if (BuildConfig.DEBUG) e.printStackTrace();
|
||||
}
|
||||
return path;
|
||||
} else if ("apk".equals(fileExtension)) {
|
||||
throw new IllegalStateException("APKs should not be handled in the media install path!");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
76
app/src/test/java/org/fdroid/fdroid/data/ApkTest.java
Normal file
76
app/src/test/java/org/fdroid/fdroid/data/ApkTest.java
Normal file
@ -0,0 +1,76 @@
|
||||
package org.fdroid.fdroid.data;
|
||||
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.fdroid.fdroid.installer.ApkCache;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
import org.robolectric.shadows.ShadowMimeTypeMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ApkTest {
|
||||
public static final String TAG = "ApkTest";
|
||||
|
||||
private static ContextWrapper context;
|
||||
|
||||
@Before
|
||||
public final void setUp() {
|
||||
context = ApplicationProvider.getApplicationContext();
|
||||
ShadowMimeTypeMap mimeTypeMap = Shadows.shadowOf(MimeTypeMap.getSingleton());
|
||||
mimeTypeMap.addExtensionMimeTypMapping("apk", "application/vnd.android.package-archive");
|
||||
mimeTypeMap.addExtensionMimeTypMapping("obf", "application/octet-stream");
|
||||
mimeTypeMap.addExtensionMimeTypMapping("zip", "application/zip");
|
||||
ShadowLog.stream = System.out;
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetMediaInstallPathWithApk() {
|
||||
Apk apk = new Apk();
|
||||
apk.apkName = "test.apk";
|
||||
apk.repoAddress = "https://example.com/fdroid/repo";
|
||||
assertTrue(apk.isApk());
|
||||
apk.getMediaInstallPath(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMediaInstallPathWithOta() throws IOException {
|
||||
Apk apk = new Apk();
|
||||
apk.apkName = "org.fdroid.fdroid.privileged.ota_2110.zip";
|
||||
apk.repoAddress = "https://example.com/fdroid/repo";
|
||||
assertFalse(apk.isApk());
|
||||
copyResourceFileToCache(apk);
|
||||
File path = apk.getMediaInstallPath(context);
|
||||
assertEquals(new File(context.getApplicationInfo().dataDir + "/ota"), path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMediaInstallPathWithObfZip() throws IOException {
|
||||
Apk apk = new Apk();
|
||||
apk.apkName = "Norway_bouvet_europe_2.obf.zip";
|
||||
apk.repoAddress = "https://example.com/fdroid/repo";
|
||||
assertFalse(apk.isApk());
|
||||
copyResourceFileToCache(apk);
|
||||
File path = apk.getMediaInstallPath(context);
|
||||
assertEquals(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), path);
|
||||
}
|
||||
|
||||
private void copyResourceFileToCache(Apk apk) throws IOException {
|
||||
FileUtils.copyInputStreamToFile(getClass().getClassLoader().getResource(apk.apkName).openStream(),
|
||||
ApkCache.getApkDownloadPath(context, apk.getCanonicalUrl()));
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.fdroid.fdroid.installer;
|
||||
|
||||
import android.content.ContextWrapper;
|
||||
import android.util.Log;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowLog;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class ApkCacheTest {
|
||||
private static final String TAG = "ApkCacheTest";
|
||||
|
||||
private ContextWrapper context;
|
||||
private File cacheDir;
|
||||
|
||||
@Before
|
||||
public final void setUp() {
|
||||
context = ApplicationProvider.getApplicationContext();
|
||||
cacheDir = ApkCache.getApkCacheDir(context);
|
||||
ShadowLog.stream = System.out;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApkCacheDir() {
|
||||
Log.i(TAG, "path: " + cacheDir);
|
||||
assertTrue("Must be full path", cacheDir.isAbsolute());
|
||||
assertTrue("Must be a directory", cacheDir.isDirectory());
|
||||
assertTrue("Must be writable", cacheDir.canWrite());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApkDownloadPath() {
|
||||
assertEquals("Should be in folder based on repo hostname",
|
||||
new File(cacheDir, "f-droid.org--1/org.fdroid.fdroid_1008000.apk"),
|
||||
ApkCache.getApkDownloadPath(context,
|
||||
"https://f-droid.org/repo/org.fdroid.fdroid_1008000.apk"));
|
||||
assertEquals("Should be in folder based on repo hostname with port number",
|
||||
new File(cacheDir, "192.168.234.12-8888/sun.bob.leela_2.apk"),
|
||||
ApkCache.getApkDownloadPath(context,
|
||||
"http://192.168.234.12:8888/fdroid/repo/sun.bob.leela_2.apk"));
|
||||
assertEquals("Should work for OTA files also",
|
||||
new File(cacheDir, "f-droid.org--1/org.fdroid.fdroid.privileged.ota_2110.zip"),
|
||||
ApkCache.getApkDownloadPath(context,
|
||||
"http://f-droid.org/fdroid/repo/org.fdroid.fdroid.privileged.ota_2110.zip"));
|
||||
assertEquals("Should work for ZIP files also",
|
||||
new File(cacheDir, "example.com--1/Norway_bouvet_europe_2.obf.zip"),
|
||||
ApkCache.getApkDownloadPath(context,
|
||||
"https://example.com/fdroid/repo/Norway_bouvet_europe_2.obf.zip"));
|
||||
}
|
||||
}
|
BIN
app/src/test/resources/Norway_bouvet_europe_2.obf.zip
Normal file
BIN
app/src/test/resources/Norway_bouvet_europe_2.obf.zip
Normal file
Binary file not shown.
BIN
app/src/test/resources/org.fdroid.fdroid.privileged.ota_2110.zip
Normal file
BIN
app/src/test/resources/org.fdroid.fdroid.privileged.ota_2110.zip
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user