robolectric
robolectric copied to clipboard
OutputStream still open Exception in ShadowPackageInstaller when using try-with-resources
Description
It raises an inappropriate SecurityException when using try-with-resources with a ShadowPackageInstallerSession. It happens because it expects that the close() is done before the session.commit(), but when we call session.commit() in the try-with-resources, the close() is not yet called (it's too early). A workaround is possible by just calling session.close() in in the try-with-resources before the session.commit() This workaround raises lint issues because using try-with-resources is a recommended way with SonarLint (https://rules.sonarsource.com/java/RSPEC-2093/).
The implied code in ShadowPackageInstaller.java :
if (outputStreamOpen) {
throw new SecurityException("OutputStream still open");
}
https://github.com/robolectric/robolectric/blob/0643c19fe8a32a4569b01a1950cf21613cb28497/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPackageInstaller.java#L265C64-L265C64
Steps to Reproduce
- Use try-with-resources with a PackageInstallerSession and an OutputStream (see code example below) :
public static final String ACTION_INSTALL_COMMIT =
"com.android.packageinstaller.ACTION_INSTALL_COMMIT";
private static final int PACKAGE_WRITE_BUFFER_SIZE = 65536;
mPackageManager = RuntimeEnvironment.getApplication().getPackageManager();
public void installPackage(File packageFile, String packageName) throws IOException {
final PackageInstaller packageInstaller = mPackageManager.getPackageInstaller();
final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(packageName);
final int sessionId = packageInstaller.createSession(params);
try (final PackageInstaller.Session session = packageInstaller.openSession(sessionId);
final OutputStream out = session.openWrite(mContext.getPackageName(), 0, -1);
final InputStream in = new FileInputStream(packageFile)) {
byte[] buffer = new byte[PACKAGE_WRITE_BUFFER_SIZE];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
session.fsync(out);
final Intent intent = new Intent(ACTION_INSTALL_COMMIT);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
}
}
Workaround : add out.close();
right after session.fsync(out);
Robolectric & Android Version
Robolectric 4.10.3 minSdkVersion 23 targetSdkVersion 29