Monday, August 26, 2019

Not a lot of options for cloning dynamic disks

I recently discovered, by checking the SMART status, that my desktop's main hard drive was beginning to fail. I didn't expect this to be tough to fix since I had an extra hard drive in the machine (used only for backup) and a copy of Symantec Ghost. When I booted into my Ghost environment, though, it didn't see the hard drives, only the flash drive I'd booted from. I tried with a couple other tools from AOMEI and EaseUs, which each saw a whole disk as one partition. After some research I found that this was due to my drives being dynamic, a conversion I must have done a long time ago while they were involved in some kind of software RAID. I tried using TestDisk to turn the dynamic partition table into a normal one, which worked on the backup drive of only one partition, but got very confused on the main drive.

I decided to try just dd'ing (a low-level copy) the failing main drive onto the good backup one. First I used fdisk -l to see which drive was which Linux device. Fortunately the drives are exactly the same size. I used this command to perform the copy and see the progress:
dd if=/dev/SOURCE of=/dev/DEST bs=1M conv=sync,noerror iflag=fullblock status=progress

It did encounter a read error from the failing drive along the way, but proceeded to completion. Concerningly, there was a "disk full" error at the very end - hopefully it didn't get off by one block somewhere in the middle.

When I rebooted into Windows, it used the backup disk (since that was numbered first by the SATA controller, I think) and brought the old disk offline due to a signature collision. So far everything seems to be working great. Due to that read error, there's probably some blank bytes in some file somewhere that will be exciting when discovered, but for now the system seems OK!

Monday, August 19, 2019

Android 4.x and earlier don't support modern TLS by default

While testing an Android app right before release, I found that on old versions of Android, it failed to complete any web requests to my server. The Volley error response indicated that the cause was an SSLException, "connection closed by peer." Apparently the only TLS version supported by Android 4.x is TLS v1.0, which was disabled on my server a while back. Google Play Services has a provider for modern TLS from which an SSL engine can be created, but I'm not yet sure how to make this work with Volley.

Sunday, August 18, 2019

Constant pool entries are reused for different kinds of constants

Names of class members in Java class files as indexes into the file's constant pool. Changing (using something like BCEL) the value of the UTF8 constant used by a member's name renames the member. However, that constant might also be used for something that's not a reference to that member, like a string literal in code or a reference to a different class's member of the same name, so that rename strategy could break things. Instead, new UTF8 and name-and-type constants should be added and the member reference constants should be adjusted to refer to those. Alternatively, a higher-level library like ASM may be more convenient.

Saturday, August 17, 2019

Name collisions due to similarly obfuscated libraries

I'm currently working with a project that has several Android AAR library dependencies, independently built and obfuscated with R8. This setup initially caused crashes with NoSuchMethodErrors for the constructor of classes with obfuscated names like a.a.a.a.a.a. Apparently the obfuscation of multiple dependencies remapped different classes to the same name, only one of which was loaded into the application. Since the classes didn't have the same shape, the uses from one dependency required members that weren't there. I worked around the problem by directing R8 to -keep one of the classes that was mapped to the collided name according to mapping.txt.

Friday, August 16, 2019

LocalBroadcastManager sendBroadcastSync crashes if a null receiver was registered

Today I saw another developer experience a NullPointerException inside sendBroadcastSync on a LocalBroadcastManager. The manager wasn't null, but it turned out some other part of the application had called registerReceiver passing a null reference. So when Android tried to deliver the intent, it tried to call a method on that null it had stored. I'm not sure of a good way to investigate this in a large codebase, but in Robolectric tests a shadow could be written that fails immediately if passed a null broadcast receiver.

Thursday, August 15, 2019

Policy Plus - Apply button fix

Today I received an email informing me of a Policy Plus bug. When the Apply button in the setting editor window is used to commit the settings but the window is then dismissed by clicking the X, the main window's policies list doesn't refresh with the new policy setting. (It does if the Cancel button is used instead.) I fixed this by adding a FormClosed listener and altering the DialogResult if appropriate, thereby indicating to the main window that it should refresh.

The change, which happens to be the hundredth commit to Policy Plus, is live on GitHub.

Tuesday, August 13, 2019

Windows DLLs' PE timestamps aren't timestamps

One user wondered why the "create time" entries for many DLLs in the output of listdlls -v were incorrect and "all over the place." I investigated and found that ListDLLs takes that time from the PE/COFF headers, confirming matches with dumpbin /headers. I'm guessing that the timestamp field is intentionally filled in with not a time, but a hash of source code or the rest of the binary to allow reproducible builds. That way, recompiling the module produces another binary that can be trivially checked for identity with the first, showing that the binary hasn't been tampered with.

Monday, August 12, 2019

Other Gradle plugins can cause earlier than expected task configuration

One of my Gradle plugins registers a task immediately at apply time with a configuration action that references tasks created later by a different plugin. This worked perfectly fine at first because of task configuration avoidance - the action didn't run until configuration was needed, at which point the things it required had taken place. It stopped working when I added another plugin to the same project, at which point my first plugin's task tried to configure itself and failed. The new plugin was iterating over all tasks in the project, which caused them all to be configured. Evidently configuration avoidance shouldn't be relied on - I'll need to fix my first plugin.

Sunday, August 11, 2019

Thread count of 64-bit processes is limited by memory

One user wondered about the limit on threads in one Windows process. In 32-bit processes, address space is the limit. On 64-bit processes, though, there's no shortage of address space. There, a process can continue creating threads until the system runs out of memory. On my machine, that's about 270,000 threads.

Saturday, August 10, 2019

Working with Groovy Nodes from Kotlin

Yesterday I was translating some Groovy Gradle buildscript to Kotlin in a project deployed to Maven Central using code like this. The block starting on line 105 is some kind of Groovy structure literal (?) that adds to a Node for an XML document. Using that from Kotlin mostly involves appendNode calls (for tags containing only text content) and Node constructions (which automatically insert the new node in the specified parent). To add desired nodes to the document, I used code like this:
asNode().apply {
  appendNode("description", "Description of the library")
  Node(this, "licenses").apply {
    Node(this, "license").apply {
      appendNode("name", "Some License")
      appendNode("url", "http://example.com/license")
    }
  }
  // and so on
}

Friday, August 9, 2019

Make sure the right POM is being signed

Today I deployed a Gradle project to Maven Central using the signing and maven-publish plugins. Unlike my previous deployments, this project used the Kotlin DSL for its buildscript. I translated the Groovy setup to Kotlin with too much trouble, but when I tried to close the staging repository, it failed validation with a signature error on the POM. I confirmed that the ASC file was uploaded correctly and that it was a valid signature for the locally generated POM. I didn't notice anything in a cursory glance over the uploaded POM, but a diff showed that the <?xml ?> tag was slightly different. Apparently the Groovy XML utilities produced an intermediate POM file that exactly matched the final one uploaded by maven-publish, but my way of doing it in Kotlin didn't, so the script produced a signature for a different file. I ended up working around it by a quick string replace.

Tuesday, August 6, 2019

Nonpublic Android resources are only hidden from autocomplete

Android libraries can set which resources are "public", exposed to other libraries or apps that use them. However, it looks like all resources for the app, no matter the defining library, go into the same pool. Even though nonpublic resources do not appear in the autocomplete of R lists, they will work perfectly fine if used anyway. Interestingly, resources identifiers with the same name are given the same numeric value across library boundaries.

Monday, August 5, 2019

Gradle sync is needed after replacing AAR file dependencies

Today I was working with an Android library dependency stored in an AAR file rather than fetched from a repository. I found strange behavior after replacing the AAR: it was like the library's code didn't match its Android resources even though inspecting the AAR showed it was correct. Things started being reasonable again after doing File | Sync Project with Gradle Files.

Sunday, August 4, 2019

Compiling one additional file with an Android Gradle JavaCompile task

I recently needed to create a Gradle task to compile a single Java file in an Android project after the normal compilation of a variant's source set was complete. I found some inspiration from this Gradle forum thread, but things are more complicated when using the Android Gradle plugin rather than the plain Java one. I'm not sure how to get the appropriate paths from the project configuration, so I grab them from an existing Java compile task for the variant. task is the new JavaCompile task; javaTask is the existing one for the variant (e.g. compileDebugUnitTestJavaWithJavac):
task.options.sourcepath = project.fileTree("src/test/java")
task.source = task.options.sourcepath!!.asFileTree
task.include("com/example/package/TheFile.java")
task.classpath = javaTask.classpath + javaTask.outputs.files
task.options.bootstrapClasspath = javaTask.options.bootstrapClasspath
task.destinationDir = javaTask.destinationDir
task.sourceCompatibility = javaTask.sourceCompatibility
task.targetCompatibility = javaTask.targetCompatibility

(That's Kotlin code; Groovy will probably be similar minus the !!.) The file can be excluded from the original compilation task's inputs like so:

javaTask.excludes.add("com/example/package/TheFile.java")