android: Properly indicate successful Qt install
This patch ensures that installed files are written to physical disk before the 'cache.version' file is written. QtLoader.java uses the 'cache.version' file written during self-installation to indicate whether re-installation is necessary. The 'cache.version' file, however, was being written at the start of installation, so its existence merely indicated that the installation was attempted. In the case of power loss during installation, the existence of 'cache.version' would prevent retrying installation on the next launch, so the bad installation was irrecoverable. [ChangeLog][Android] Fixed an issue where an application installation would be irrecoverably broken if power loss or a crash occurred during its first initialization run. Fixes: QTBUG-71523 Change-Id: If771b223a0a709a994c766eea5a4ba14ae95201e Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
093a1a6232
commit
327c8a805e
@ -159,6 +159,9 @@ public abstract class QtLoader {
|
|||||||
protected ComponentInfo m_contextInfo;
|
protected ComponentInfo m_contextInfo;
|
||||||
private Class<?> m_delegateClass;
|
private Class<?> m_delegateClass;
|
||||||
|
|
||||||
|
private static ArrayList<FileOutputStream> m_fileOutputStreams = new ArrayList<FileOutputStream>();
|
||||||
|
// List of open file streams associated with files copied during installation.
|
||||||
|
|
||||||
QtLoader(ContextWrapper context, Class<?> clazz) {
|
QtLoader(ContextWrapper context, Class<?> clazz) {
|
||||||
m_context = context;
|
m_context = context;
|
||||||
m_delegateClass = clazz;
|
m_delegateClass = clazz;
|
||||||
@ -357,7 +360,7 @@ public abstract class QtLoader {
|
|||||||
|
|
||||||
AssetManager assetsManager = m_context.getAssets();
|
AssetManager assetsManager = m_context.getAssets();
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
OutputStream outputStream = null;
|
FileOutputStream outputStream = null;
|
||||||
try {
|
try {
|
||||||
inputStream = assetsManager.open(source);
|
inputStream = assetsManager.open(source);
|
||||||
outputStream = new FileOutputStream(destinationFile);
|
outputStream = new FileOutputStream(destinationFile);
|
||||||
@ -369,8 +372,12 @@ public abstract class QtLoader {
|
|||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
|
||||||
if (outputStream != null)
|
if (outputStream != null)
|
||||||
outputStream.close();
|
// Ensure that the buffered data is flushed to the OS for writing.
|
||||||
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
// Mark the output stream as still needing to be written to physical disk.
|
||||||
|
// The output stream will be closed after this sync completes.
|
||||||
|
m_fileOutputStreams.add(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void createBundledBinary(String source, String destination)
|
private static void createBundledBinary(String source, String destination)
|
||||||
@ -388,7 +395,7 @@ public abstract class QtLoader {
|
|||||||
destinationFile.createNewFile();
|
destinationFile.createNewFile();
|
||||||
|
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
OutputStream outputStream = null;
|
FileOutputStream outputStream = null;
|
||||||
try {
|
try {
|
||||||
inputStream = new FileInputStream(source);
|
inputStream = new FileInputStream(source);
|
||||||
outputStream = new FileOutputStream(destinationFile);
|
outputStream = new FileOutputStream(destinationFile);
|
||||||
@ -400,8 +407,12 @@ public abstract class QtLoader {
|
|||||||
inputStream.close();
|
inputStream.close();
|
||||||
|
|
||||||
if (outputStream != null)
|
if (outputStream != null)
|
||||||
outputStream.close();
|
// Ensure that the buffered data is flushed to the OS for writing.
|
||||||
|
outputStream.flush();
|
||||||
}
|
}
|
||||||
|
// Mark the output stream as still needing to be written to physical disk.
|
||||||
|
// The output stream will be closed after this sync completes.
|
||||||
|
m_fileOutputStreams.add(outputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion)
|
private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion)
|
||||||
@ -449,27 +460,6 @@ public abstract class QtLoader {
|
|||||||
if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion))
|
if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
{
|
|
||||||
File versionFile = new File(pluginsPrefix + "cache.version");
|
|
||||||
|
|
||||||
File parentDirectory = versionFile.getParentFile();
|
|
||||||
if (!parentDirectory.exists())
|
|
||||||
parentDirectory.mkdirs();
|
|
||||||
|
|
||||||
versionFile.createNewFile();
|
|
||||||
|
|
||||||
DataOutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
outputStream = new DataOutputStream(new FileOutputStream(versionFile));
|
|
||||||
outputStream.writeLong(packageVersion);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
if (outputStream != null)
|
|
||||||
outputStream.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// why can't we load the plugins directly from libs ?!?!
|
// why can't we load the plugins directly from libs ?!?!
|
||||||
String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
|
String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
|
||||||
@ -499,6 +489,66 @@ public abstract class QtLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Java compiler must be assured that variables belonging to this parent thread will not
|
||||||
|
// go out of scope during the runtime of the spawned thread (since in general spawned
|
||||||
|
// threads can outlive their parent threads). Copy variables and declare as 'final' before
|
||||||
|
// passing into the spawned thread.
|
||||||
|
final String pluginsPrefixFinal = pluginsPrefix;
|
||||||
|
final long packageVersionFinal = packageVersion;
|
||||||
|
|
||||||
|
// Spawn a worker thread to write all installed files to physical disk and indicate
|
||||||
|
// successful installation by creating the 'cache.version' file.
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
finalizeInstallation(pluginsPrefixFinal, packageVersionFinal);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(QtApplication.QtTAG, e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finalizeInstallation(String pluginsPrefix, long packageVersion)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// Write all installed files to physical disk and close each output stream
|
||||||
|
for (FileOutputStream fileOutputStream : m_fileOutputStreams) {
|
||||||
|
fileOutputStream.getFD().sync();
|
||||||
|
fileOutputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fileOutputStreams.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create 'cache.version' file
|
||||||
|
|
||||||
|
File versionFile = new File(pluginsPrefix + "cache.version");
|
||||||
|
|
||||||
|
File parentDirectory = versionFile.getParentFile();
|
||||||
|
if (!parentDirectory.exists())
|
||||||
|
parentDirectory.mkdirs();
|
||||||
|
|
||||||
|
versionFile.createNewFile();
|
||||||
|
|
||||||
|
DataOutputStream outputStream = null;
|
||||||
|
try {
|
||||||
|
outputStream = new DataOutputStream(new FileOutputStream(versionFile));
|
||||||
|
outputStream.writeLong(packageVersion);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (outputStream != null)
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteRecursively(File directory)
|
private void deleteRecursively(File directory)
|
||||||
|
Loading…
Reference in New Issue
Block a user