Tuesday, December 31, 2019

Parceling and unparceling is not necessarily deep cloning

Serializing something to a temporary buffer then deserializing it is often an easy way to duplicate an object that supports serialization. Today I was trying to make independent copies of a handful of different Parcelable classes, so I tried writing a general function to parcel them then read them back out. I ran into trouble with testing because my Robolectric shadows of IBinder classes the parcelables referred to contributed state that did not get written to the parcel. However, I realized another problem: parcels can transfer active IBinder objects. These will be reconstituted into proxies that call into the original object, foiling the plan of creating a deep, independent copy for an arbitrary Parcelable.

Monday, December 30, 2019

Putting an array on the pipeline with the unary comma operator

When output onto the pipeline, PowerShell arrays tend to explode into individual pipeline items. I used to deal with this by passing arrays as parameters instead. Recently, though, I heard about the unary comma operator. Placing a single comma before any value, including an array, produces a single-element array containing the value. When that explodes onto the pipeline, it releases the original array.

Sunday, December 29, 2019

When Android Studio can't open the run configurations dropdown

I was doing some quick testing using a class with a main method in Android Studio, launching it with the run button in the margin, which creates an ad hoc run configuration. I then Refactor | Rename'd the class, at which point an IDE Error occurred, probably because there was suddenly no class with the most recently run name. (I assume the same thing would have happened in IntelliJ.) After this, even trying to pull down the run configurations dropdown to pick a different one didn't work.

To reset the workspace, I had to close Android Studio, delete the workspace.xml file from the .idea directory, and reopen the IDE. That cleared various things including the ad hoc run configurations.

Saturday, December 28, 2019

Getting Logcat output from Robolectric tests

By default, Logcat output (from Log calls) doesn't seem to go anywhere when running tests under Robolectric. To see it, the log stream has to be set:

ShadowLog.stream = System.out;

However, there might be undesired spurious output from Android or third-party components. One way to deal with this would be to set the stream to a custom one that filters the output in some way before writing it to standard out. Alternatively, you could provide a custom ShadowLog implementation that filters by tag or call stack. One of my projects checks to see if the current thread's stack trace has a class in the app's package right before Log. To avoid unnecessary checks, the result on whether the log is "internal" is cached by tag.

Friday, December 27, 2019

Seagate SeaTools can help test other brands of drives

Today I needed to check the health of a drive whose manufacturer doesn't seem to distribute diagnostic software specifically for it. I would have looked over the SMART information with Speccy, but the free version is for home use only and I was working with a business. I had heard good things about Seagate's SeaTools, so I tried that. It was able to perform a handful of checks on the drive which were helpful.

Thursday, December 26, 2019

Jackson KotlinModule signature changed between versions

I'm working with several Gradle plugins that use Jackson for JSON and/or YAML parsing. They're written in Kotlin, so they take advantage of the Jackson Kotlin module. I found after trying to upgrade some plugins' dependencies that Gradle sync always crashed with a NoSuchMethodError regarding the KotlinModule constructor. It appears that that constructor gained a new optional parameter - which changes its JVM method signature - in multiple versions, which is a binary-incompatible change. That makes differences in dependency version very likely to cause crashes. Updating some but not all plugins to 2.10.x causes a problem; 2.9.8 and 2.9.9 seem to be fine.

Wednesday, December 25, 2019

Provided Robolectric shadows can be replaced

Robolectric provides shadows of many Android classes and has a way to register custom shadows. Today I needed more functionality from a shadow that already existed. Fortunately, it seems that any user-provided shadows (registered in robolectric.properties as usual) replace the defaults.

Wednesday, December 18, 2019

When Get-Content doesn't recognize the -Encoding parameter

PowerShell's Get-Content cmdlet can read NTFS alternate data streams either with the -Stream parameter or by putting the stream after a colon in the path. Using the latter approach and trying to get the data of a file in the current directory as a byte array, someone mentioned that they received an error saying "a parameter cannot be found that matches parameter name 'Encoding'". This turned out to be because the path wasn't being interpreted as a file system path because it wasn't qualified - the .\ was missing. So the file system provider wasn't being used, and apparently that provider is responsible for some dynamic parameters like -Encoding. Qualifying the path fixed the problem.

Wednesday, December 4, 2019

When Android Studio says "this project does not use the Gradle build system" in the presence of build.gradle

Today I helped a student who was seeing some strange behavior from Android Studio. The IDE appeared to be mostly working, but compilation failed with errors about being unable find several items that all happened to be provided by a library.

Opening the project produced a "Migrate to Gradle" suggestion stating that "this project does not use the Gradle build system." Accordingly, there was no File | Sync Project with Gradle Files menu item. The project had been created as a Gradle project. I was shown screenshots that confirmed the existence and contents of all expected build.gradle files, which was very confusing because the migration suggestion appeared even after clearing all caches I could think of.

Staring very intently at the screen eventually revealed a subtle problem: the parent directory of the project had been opened, not the project itself. Since there was no build.gradle in the opened root directory, Android Studio rightfully did not consider it a Gradle project, but allowed setting the Android SDK manually (which is what the student must have done to be able to attempt compilation). Opening the correct location in the directory hierarchy fixed the problem.

Thursday, November 21, 2019

Windows comes with the tar utility now

Recently I needed to extract a tar.gz archive on Windows as part of an automated pipeline. I tried fiddling with 7-zip and WinRAR's command-line executable but hoped for something more straightforward. I had the Linux subsystem installed, and I knew about the tar utility, so I poked around and accidentally noticed that there's a tar provided by Windows even outside WSL. tar xzf works for extracting tar.gz files from the Windows command line just like on Linux.

Friday, November 15, 2019

Policy Plus - Several fixes

I had some fun today fixing a couple issues with Policy Plus. I previously thought that the undocumented multiTextBox presentation element had no attributes other than ID, but it turns out to sometimes have a label as the tag's inner text. Policy Plus will now display the multiline text box's label if specified.

While double-checking that multiTextBoxes work with Semantic Policy, I noticed that Semantic Policy import was broken for user policies; all policies were always imported to the machine policy source. That was an easy fix.

The changes are live on GitHub.

Saturday, November 2, 2019

"Found cyclical include chain" can be caused by tools:layout

Today I helped a student who was getting a "found cyclical <include> chain" error from the Android Studio UI designer. Despite the error message, there was no <include> tag in the layout's XML. Rather, the self-inclusion was from a tools:layout attribute, set as the layout attribute in the Attributes pane of a fragment.

Wednesday, October 9, 2019

Android Studio incorrectly reformats Kotlin buildscripts

In the assignment they're currently working on, I saw some students create an activity in Android Studio and then receive script compilation errors in the project. The starter code we gave them contains a Kotlin DSL Gradle script which was altered by Android Studio when the activity was added. Strangely, the exclamation mark in !in was moved before the preceding space, making it invalid Kotlin. Reverting that change and doing a Gradle sync fixed the problem.

Thursday, October 3, 2019

"Unknown fragments" errors in the Android UI designer are normal

A student was poking around Android Studio today and asked about an "unknown fragments" error in the UI designer. This seems to be normal when including a fragment which has no layout to use as a preview, like the Google Maps SDK map fragment. Such fragments will appear as a dark gray box in the UI designer, but can work perfectly fine at runtime.

Monday, September 30, 2019

Android Chrome may not redirect to custom URI schemes automatically

Single Sign-On authentication for the Moodle mobile app works by opening the SSO page in a browser window and trusting the browser to redirect after the login to a custom URI scheme handled by the app. Interestingly, this works when the user was already logged into Moodle in their browser, but not after completing the SSO flow. Apparently the Chrome browser sometimes won't honor an HTTP Location redirect to a custom URI scheme. In Moodle's case, the confirmed=true query parameter can be added to make the page display some nice UI with a link to proceed to the app.

Sunday, September 29, 2019

Poking at the Markeen-assisted levels project

Many months ago, I started a Keen 5 level pack based on maps randomly generated by Markeen. It was coming along well but I left it alone for a long time because of other commitments. I had a couple moments of free time this week to look back at it. I had done my first pass of cleanup on the eighth map, but hadn't decided the flow of play through it. The constraints given to me by Markeen seem to require it to be an easier level than most others, but that's probably OK.

Wednesday, September 18, 2019

When Windows 10 upgrades to Enterprise instead of Pro

One user was trying to upgrade Windows 10 Home to Pro and so purchased a Pro upgrade in the Microsoft Store app. But after the installation, the Enterprise edition was installed and not activated because the user didn't have an Enterprise license. A clean reinstall of Windows 10 Home with another attempt at installing the Pro license from the Store produced Enterprise again. Apparently the way to downgrade to Pro is to use "change product key" to set the generic activation key for Pro. It will then notice the digital license and activate Pro properly.

Monday, September 16, 2019

CloudFront default objects are configured separately from S3 index documents

I recently helped someone set up a static site hosted on Amazon S3 with Amazon CloudFront as an SSL terminator. S3 buckets have an option for the index document (the page served when someone visits the root of the website). We set that, but found that going to the site's root produced an Access Denied error page, but going directly to a specific page worked. The CloudFront distribution apparently has a setting for default object which we had not set; CloudFront doesn't consult the S3 index document setting.

Saturday, September 14, 2019

Endless Gradle sync can indicate a missing Android SDK version

I previously noticed that if the Android SDK version requested by an Android Studio Gradle project isn't installed, Gradle sync may appear to succeed but everything will be broken. Today I found that another possible symptom is Gradle sync never finishing. A bunch of green check marks appear in the Build window, but a wheel still spins and Android Studio never proceeds to indexing. This can be fixed by installing the needed SDK version in the SDK Manager and pressing the Sync Project with Gradle Files button.

Wednesday, September 11, 2019

When using Git from IntelliJ or Android Studio fails with "illegal character <:>"

I recently helped some students who were trying to work with Git from Android Studio but received "illegal character" errors mentioning index 5 and the colon character. This turned out to be from a mistake in installing Git and trying to connect it to Android Studio. Where the IDE looks for Git is controlled by the "path to Git executable" setting on the Git page of the Version Control section of the settings. Pasting a Git repository URL there (in an attempt to clone it) causes this error. Instead the path box can be left blank to make Android Studio try to automatically find the Git installation.

Sunday, September 8, 2019

When Android Gradle sync fails with "could not get unknown property 'com'"

Today I helped a student who couldn't build any Android Studio projects because sync always failed with "could not get unknown property 'com'", with the stack trace not referring to the user's Gradle buildscripts. The normal strategy of invalidating Android Studio caches didn't help, nor even did reinstalling Android Studio! After a lot of poking around, we fixed the problem by removing the .gradle folder from the user's home directory. Apparently something in the shared Gradle cache was corrupt.

Wednesday, September 4, 2019

"Module not specified" may be caused by a missing Android SDK

Android SDK 29 came out pretty recently; one of my projects has not been updated to target it yet. But installing Android Studio now installs only version 29 of the SDK by default. Opening that project without the 28 SDK appears to work in that Gradle sync claims to complete successfully, but there is no "app" run configuration and test run configurations fail to start, saying "module not specified." Sure enough, the app directory isn't recognized as an IDEA module. Opening certain project files shows the "Gradle sync failed" bar, but trying sync again still claims to succeed. The only clue is a warning logged in the text version of the Gradle sync output that the license for SDK 28 hasn't been accepted.

Fixing this just requires installing the Android 28 SDK. That can be done in the Android Studio settings: Appearance & Behavior → System Settings → Android SDK.

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")

Monday, July 29, 2019

Kotlin Gradle buildscripts can't easily apply arbitrary plugins from other repositories

Previously I published a Gradle plugin to Maven Central. It had been working fine in Groovy buildscripts, but using it from a Kotlin buildscript was tricky. It registers an extension, so using the apply function to apply it didn't work; the extension needed the plugin already applied to even compile. The plugins block didn't immediately work because it looked for a plugin in the Gradle Plugins Portal, or through some kind of placeholder/marker artifact of a different name in other repositories.

The way to deal with this is to add a pluginManagement block to settings.gradle to set which artifact coordinate Gradle will use to look for the plugin of a given ID. repositories should specify the repository containing the plugin plus gradlePluginPortal() for any other plugins. resolutionStrategyeachPlugin is run for every requested plugin and can intercept and change the artifact coordinate. The requested plugin information can be checked to see if it's the tricky one; if so the useModule function should be called to set the artifact coordinate.

Sunday, July 28, 2019

The phpBB 2 to 3 converter might blank a few posts with non-ASCII characters

Today I received a report from a user on a forum I host that a couple old posts appeared blank. These were made before I migrated the forum from phpBB 2 to phpBB 3, a transition that was made tricky by a problem with non-ASCII character encoding. Examining the phpbb_posts table showed that the affected posts' post_text had been replaced with <?xml version="1.0"?> plus a linefeed. Consulting a backup of the phpBB 2 database, I saw that both posts originally contained non-ASCII characters that get encoded into multiple bytes by UTF-8.

Wednesday, July 24, 2019

Detecting whether an Android ID is defined

I'm writing some test suites for student Android code. I'm trying to minimize the tests' compile-time dependencies on student code so that students can work on one part at a time without having compilable versions of everything. Some assignments ask them to set up layouts, in which view IDs create constants on R.id. The integer value of an ID constant can be looked up by name using the getIdentifier method of Resources. It can be called with all information in the first parameter...

resources.getIdentifier("com.example.app:id/theName", null, null)

...or with the resource type and package in separate parameters...

resources.getIdentifier("theName", "id", "com.example.app")

The return value is zero if the ID doesn't exist. That can be checked and used to throw a nice error message at runtime when that test is run rather than a compile error.

Monday, July 22, 2019

PowerMock stubbing can help deal with subclasses of private classes

Today I needed to mock an inner class that was a subclass of a different, private inner class. Creating a mock or spy failed in the class initializer because the generated mock class didn't have access to the private superclass. However, using PowerMockito.stub on a PowerMockito.method representation of the method to replace worked.

Ionic needs a full restart for some changes

The Ionic web server has a slick feature that detects changes to the source files, recompiles the appropriate components, and delivers the changes to the browser automatically. However, this does not appear to work for all changes. In particular, I've seen adding new TypeScript files cause strange behavior that was resolved by stopping and restarting ionic serve.

Saturday, July 20, 2019

Working with support library AlertDialog in Robolectric

Robolectric provides a ShadowAlertDialog class with a getLatestAlertDialog function to get the last Android AlertDialog. However, in my project that uses a support library (AndroidX), that function returned null. To get the last shown dialog, I had to use ShadowDialog.getLatestDialog and cast that Dialog to the AlertDialog type from the support library.

Wednesday, July 17, 2019

NullPointerExceptions from PowerMock can indicate a non-mocked static class

Today I was writing some PowerMock-based test code and encountered a NullPointerException in the middle of normal-looking and previously-working mock configuration for a static method. Another test in the same test suite was failing with an UnfinishedStubbingException in something that also looked reasonable. It turned out that I had forgotten to PowerMockito.mockStatic the class in that test.

Tuesday, July 16, 2019

Exotherm - Need a new host

Mercuric lost connection with FICS some time today. To see if it crashed from a Python exception, I looked at the log, but the file was empty. Previously I had suspected that the OS was terminating it to reclaim memory, but then I wondered if some management service was terminating long-running processes since this kind of server is usually just used for web hosting. I did some research and discovered that the hosting company does not like standalone long-running processes, which is fair since their plans are designed for web sites. I shut the bot down for now and will need to find a new home for it - so far PythonAnywhere looks promising.

Monday, July 15, 2019

Exotherm - Can't investigate disconnection when it doesn't disconnect

Mercuric usually disconnects from FICS after about a day, but it's somehow still up after I added logging yesterday. Looks like I'll have to wait a little longer before I can determine whether the random disconnects are due to crashes.

Sunday, July 14, 2019

Exotherm - Investigating disconnection

For quite a while, I've been planning to look more into why Exotherm (deployed as "mercuric") disconnects from the FICS after a few hours. Today I finally did something about it: in case the disconnection is due to a crash, I added a bit of code to log any errors to a file before exiting. The bot will probably have fallen over by morning - then I can check the log to see if a bug is causing it to crash.

Robolectric shadows can help mock final classes

PowerMock can mock final classes, but this requires rewriting them into a different classloader. In some cases, like getting a SupportMapFragment from the Google Maps SDK, PowerMock's reload leads to errors saying a something can't be cast to its own type. (The class gets loaded twice and instances are not convertible between the two.) Adding the class to @PowerMockIgnore fixes that, but then PowerMock can no longer work with it.

Fortunately, Robolectric does its own bytecode manipulation. Shadows can replace methods from Android classes. In the process of installing a shadow for a class, Robolectric seems to make it non-final. So declaring an empty shadow is sufficient to allow Mockito.mock to create mocks of it.

@Implements(GoogleMap.class)
public class ShadowGoogleMap { }

The shadow class needs to be registered either in robolectric.properties or in the shadows property of the @Config annotation on the test suite.

Saturday, July 13, 2019

Finalizers could be problems for sandboxing

The finalize method of any Java object will run after garbage collection when all references to the object have been lost. This occurs on a different "finalizer" thread at an unspecified time. So if an untrusted task is run in a sandboxed thread/ThreadGroup and terminated, there may still be objects waiting to be finalized. If sandboxing is based on ThreadGroup, the untrusted code there will no longer be sandboxed. Even if the sandboxing is based on class loader, an infinite loop could hang the finalizer thread, interfering with finalization of other objects. To prevent these problems, my organization uses bytecode manipulation to drop finalize methods from untrusted classes before running them in a sandbox.

Wednesday, July 10, 2019

A timed out thread pool task isn't necessarily canceled

I'm involved in a JVM application that uses an Executor to run a task and calls get with a timeout. This is done as part of a "dry run" to make sure a component is reasonably well behaved and doesn't take ages/forever to run. It was hoped that get would cancel the task if it failed to complete in time, but it does not. Rather, the calling thread is allowed to proceed, but the task thread is still going in the background, possibly in an infinite loop. As a workaround, the Callable we run on the other thread records its Thread so that the main thread can stop it if the task times out. Of course, this doesn't work if the code under test catches ThreadDeath, but ours is known to be only possibly-mistaken, not malicious.

Tuesday, July 9, 2019

Policy Plus - Dependencies cleanup

I recently noticed that Policy Plus had dependencies on a few .NET assemblies from which it never used anything. In the interest of cleanliness I removed those.

I also investigated a report of an "an item with the same key has already been added" error when loading policy definitions. I was unable to reproduce it; IDs are qualified by the target namespace of the ADMX file that defines them and defining a duplicate target namespace produces a different error. I'll remain on the lookout for problems related to that system.

Sunday, July 7, 2019

PowerMock indirectly allows setAccessible on any method

The CS class I'm on staff for is looking into ways to grade Java Android app-based assignments securely. Restricting student code without breaking Robolectric is tricky - we need to allow e.g. reflection when done by Robolectric or the test suites but not when done by student code. Some of our test suites use PowerMock, which of course internally exercises all kinds of permissions. Unfortunately it could be used by student code to override access modifiers and call arbitrary methods: WhiteboxImpl provides the public function getAllMethods, which makes all a class's methods accessible and returns them.

So checking whether trusted frameworks are using the dangerous permissions is insufficient. Fortunately, we make PowerMock a testImplementation dependency, so it's not on the compile classpath for the student sources. While students could access it at runtime via reflection (the problematic method is public), our SecurityManager can check whether PowerMock is being invoked reflectively and deny permission if so.

Saturday, July 6, 2019

Java MethodHandles can work like reflection

I'm helping test a Java execution sandbox. We want to allow untrusted code to use streams, so we can't deny the suppressAccessChecks and accessDeclaredMembers permissions, so instead we made the classloader for untrusted code reject anything in the java.lang.reflect package. Today I found that the MethodHandles API allows essentially the same capabilities as reflection. A lookup object obtained through privateLookupIn on a class belonging to a classloader that is an ancestor of the sandbox classloader can bypass the class lookup restrictions. Handles can then be obtained to normal reflection API methods, which can be used to override access modifiers.

Blocking the entire java.lang.invoke package isn't viable because streams/lambdas need parts of it, but blocking MethodHandles and MethodHandles.Lookup should make it impossible to dynamically invoke arbitrary methods.

Friday, July 5, 2019

When Kotlin tests fail with "class not found" in Android Studio

Today I was setting up a new Android Studio project. For reasons, the app had to be in Java, but I wanted to write the test suite in Kotlin. The test class and test methods had the run buttons in the margin, and the build appeared to go smoothly, but trying to run them produced a "class not found" error instead of test results. It turns out I had forgotten to apply the kotlin-android plugin. (That plugin comes from the usual Kotlin buildscript dependency.) It's also important to add the Kotlin standard library as a normal dependency, as testImplementation in my case.

Calling MethodHandle#invoke from Kotlin

Java has a class called MethodHandle which represents a dynamic but strongly typed operation. It provides invoke and invokeExact methods that are "signature polymorphic" and handled specially: rather than treating their invocations as the use of a normal varargs method, the compiler emits bytecode involving the invokedynamic instruction.

Since calls to these methods require compiler support, other JVM language compilers may not handle them properly. Kotlin 1.3.x doesn't generate the special bytecode by default and therefore cannot call signature-polymorphic methods correctly. However, the correct behavior can be enabled with the -XXLanguage:+PolymorphicSignature argument to the compiler. Alternatively, the similar invokeWithArguments method can be called normally, but may be a little slower due to the extra work of transforming the normal varargs call into a dynamic invocation.

Wednesday, July 3, 2019

Fixing the signing plugin on Gradle 5.1 and later

I previously found that using the maven-publish and signing Gradle plugins to publish to Maven Central did not work on Gradle versions 5.1 and newer: the signArchives task failed with a "duplicate key" error. So for a while I had to use the Gradle 5.0 wrapper to deploy. More recently I decided to investigate more thoroughly, and came up with a fix. Conveniently, the list of signatures is available from the signing task. Rather than this simple configuration block...
signing {
    sign configurations.archives
}

...I remove duplicates...
signing {
    def signTasks = sign configurations.archives
    signTasks.each { t ->
        def signatures = t.getSignatures()
        signatures.removeAll { oneSig ->
            signatures.count { anotherSig -> oneSig.getClassifier() == anotherSig.getClassifier() } > 1
        }
    }
}

...allowing the signing and upload to complete successfully.

The Robolectric RuntimeEnvironment isn't accessible from a different classloader

Today I worked on a SecurityManager that oversees the execution of Robolectric tests. In one check, I needed to consult the RuntimeEnvironment of the test. Trying to simply use members of that class gave wrong results; it appeared that no tests had been started yet. The problem was a difference of classloader. Robolectric loads reloads tests into a "sandbox" class loader to provide SDK isolation. Each loader got its own RuntimeEnvironment with its own set of static members. Since the SecurityManager was loaded in the system loader, it always referred to the copy in the system loader, which isn't used for tests. The solution was to go through the class context to get the test class loader, then use reflection on that to get the desired properties.

Tuesday, July 2, 2019

Robolectric's sandbox classloader doesn't preserve the ProtectionDomain

Today I investigated why my organization had been having trouble applying a ProGrade security policy to Robolectric tests. The policy we used for previous projects granted and denied rights to the untrusted code using codeBase directives, but they seemed to have no effect. It turns out that Robolectric "sandboxes" code by SDK version by reloading classes into different classloaders. The sandbox classloader doesn't seem to preserve the ProtectionDomain/CodeSource of the sandboxed classes, so the information about their origin in an untrusted location is lost. Since we control which directory structures (and therefore packages) can be compiled, I'm working on writing a SecurityManager that discriminates by class/package.

Sunday, June 30, 2019

Policy Plus - Refresh on Home

Yesterday I found that the WM_SETTINGCHANGE message is sent after a policy refresh to tell applications to detect the new policy. On Home editions Policy Plus uses its own POL-to-Registry applier rather than calling RefreshPolicyEx, so it didn't cause the message to be broadcast. That required Home users to logout/login or reboot after changing policies, which was unfortunate. As suggested by a user I made Policy Plus broadcast that message after applying the policy changes.

The change is live on GitHub.

RefreshPolicy/RefreshPolicyEx send WM_SETTINGCHANGE even on Home editions

RefreshPolicy (or RefreshPolicyEx) causes Windows to examine the Group Policy POL files and apply changes to the Registry. System applications like Explorer are notified of the update. On Home editions, the system does not have the Group Policy infrastructure to apply POLs, but once the Registry is updated, the refresh-policy functions can trigger some applications to see the changes. The functions seem to broadcast WM_SETTINGCHANGE, even on Home editions.

Saturday, June 29, 2019

Exotherm - New best rating achieved

Mercuric, the current FICS deployment of Exotherm, reached a new best rating of 1683 yesterday. It lost a few games after that peak, including one by disconnection, and is now at 1665. Once I find a way to fix the disconnections in the middle of games its rating should increase more.

Friday, June 28, 2019

Java thread stoppage can be blocked by an error handler

Calling stop on a thread object in Java isn't the same as terminating the thread from the OS perspective. Rather, it causes Java to throw a ThreadDeath wherever the target thread currently is. This throwable goes up the call stack like a normal throwable. If it is caught (e.g. by catching all Errors or all Throwables) and not rethrown, the thread can continue.

Wednesday, June 26, 2019

JavaFileManager inferBinaryName isn't used for much

The inferBinaryName method of the JavaFileManager for an in-memory compilation task is called for each file needed for compilation. If custom JavaFileObjects are being used, inferBinaryName needs to be able to handle them. However, it appears that the resulting name isn't used for very much. The part after the last dot is interpreted as the class name and checked for validity as a Java identifier. If it's not valid, the class - at least in the Java version I was working with - is not "included". But after that check, the name doesn't seem to be used for anything.

Monday, June 24, 2019

Policy Plus - ADML language override

Normally Policy Plus detects the user's locale and tries to load ADML files for that language. A Policy Plus user requested the ability to change that behavior, which would be helpful when looking for a policy mentioned in a guide written in another language. So I added a simple dialog to change the preferred language:


If a preferred language code has been set, Policy Plus will use that instead of the locale name.

This change is live on GitHub.

Sunday, June 23, 2019

FMod - Side tileinfo customization

One Abiathar user told me today that their mod uses nonstandard side tileinfo values to trigger special behavior. Abiathar (and FleexCore2) turn all nonzero left and right tile property values into 1, which is a problem if those fields are to be used for something more than "blocking vs. not".

So today I changed FleexCore2 to have byte Left and Right fields rather than Boolean SolidLeft and SolidRight, then adjusted Abiathar's tileinfo translation functions to account for non-Boolean side values. In the interest of backward compatibility to extensions, I kept the functions that take Booleans; they now delegate to the byte-accepting versions, passing 0 or 1.

Virtually all users will only need binary side properties, so rather than entirely replacing the "Solid" checkboxes in the Tile Property Modifier UI I made dropdowns only appear if configured or if that tile has nonbinary side properties. Like with top, bottom, and special properties, custom side properties can be added to the ADEPS file and will appear in the dropdowns.

I also noticed that canceling the entry of a custom tile property value reset the dropdown to the first entry rather than leaving it where it was before. That's now fixed.

Saturday, June 22, 2019

Ktor's default response is 404 even if there's a route

I'm using the Ktor web server/framework to write a web service in Kotlin. I added a handler for a certain route, put some code in it, but found that clients got a 404 error when accessing that path. Apparently the problem was that, since I didn't need to send any data to the client when handling that request, I never used one of the response methods on call - the status code defaulted to 404. Setting the status code with call.response.status produced my expected behavior.

Thursday, June 20, 2019

Some Java permission checks need to be requested by the classloader

Today I was working with a custom SecurityManager implementation, trying to make it restrict one thread group's access to a package. It appeared that checkPermission was not called with a relevant permission during the loading or execution of restricted code, nor was checkPackageAccess called. However, after the custom classloader responsible for loading the untrusted code was adjusted to make a call to the system security manager regarding the class requested by loadClass, class access could be controlled.

Tuesday, June 18, 2019

Trying to use Android UI components from the wrong thread may do something strange

Today I was making an Android app react to websocket events. When I tried to update UI based on the events, I found strange behavior like the layout not updating when a view was set to gone even though the view was no longer visible. Then I realized that the websocket library delivers the events on a different thread. Using a Handler to enqueue the UI-related work for the main thread fixed the problem.

Monday, June 17, 2019

JitPack requires the Maven plugin

A couple days ago I was trying to help make a Java library available through JitPack, an adapter that acts as a Maven repository and builds artifacts from a Git code repository. The JitPack Gradle log seemed to say the build failed with an exception about not being able to find one of the plugins the project used. However, we eventually found that the further-up warning about the lack of maven plugin was much more important. Applying that fixed the problem and allowed the project to build.

Saturday, June 15, 2019

Kotlin won't create SAM literals of interfaces defined in Kotlin

In a recent Kotlin project I wrote an interface definition meant to be consumable from Java and possibly other Kotlin code. It contains only one method, so I expected it to be usable in Kotlin code as a Single Abstract Method implementation literal, e.g. SomeInterface { arg -> ... }. This didn't compile, noting that the interface type has no constructors. Apparently interfaces defined in Kotlin cannot be used in this fashion, possibly as an encouragement to just pass functions instead.

Friday, June 14, 2019

BCEL Repository.lookupClass can't find dynamically generated classes

Recently I used Apache BCEL (Bytecode Engineering Library) to modify classes and load the altered versions. That process starts with Repository.lookupClass call to get a BCEL JavaClass instance representing a class. However, when I tried to do that to a dynamically generated class (actually one that had already been through an editing process) rather than one loaded normally by the system classloader, it couldn't find the class. If I understand correctly, lookupClass searches the application classpath and loads a .class file from disk? That will clearly not work for anything dynamically generated, so the alternate route of passing a byte stream to ClassParser is needed. This works if you can keep track of the bytecode of the edited classes you create, but isn't possible for classes created by others.

Thursday, June 13, 2019

Inner class modifiers are in two places

Today I used Apache BCEL to remove the final modifier from some Java classes, including some inner classes. I successfully altered the modifiers on the ClassGens, but when I loaded the classes I found that the inner ones were still final. Then I remembered that the InnerClass records (in the InnerClasses attribute) have an "inner access flags" property. Once I checked for and removed the final modifier from those, the loaded inner classes were non-final.

Wednesday, June 12, 2019

Conditional breakpoints are extremely slow

Today I was investigating an issue in a JVM application (written in Kotlin but JVM-based nonetheless) and added a conditional breakpoint in IntelliJ to stop at a loop iteration I was interested in. That worked as intended, but I noticed that the program then took dramatically longer to run and that the icon of the Debugger tab in the lower left blinked several times. If I understand correctly, a conditional breakpoint needs to break into the debugger every time, evaluate the condition, and resume execution if appropriate. If I needed the conditional-stop to work faster, I would have added an if statement to the code and set a normal breakpoint on a statement inside that to avoid unnecessary debugger entrances.

Tuesday, June 11, 2019

Intel communication drivers affect Windows audio stuttering, somehow

Recently, seemingly out of nowhere, my Windows 10 laptop's audio started stuttering/glitching every few seconds, regardless of the application producing the sound. A lot of people have encountered similar problems and there doesn't seem to be a consistent fix. After some reading and unsuccessful experimenting I came across a Microsoft Answers thread with a post (by "D Eddy") mentioning the Intel Driver Update Utility. Like that author, I had an outdated Intel Wireless Bluetooth driver. Even though neither audio device I tried (or any of my devices I'm aware of) used Bluetooth, installing that driver somehow fixed the audio problem.

The updater also suggested updates for my WiFi driver and Intel Rapid Storage Technology, but after I tried installing those the Rapid Storage Technology installer failed with an unspecified error and my computer couldn't boot after the restart. (Good thing I had a recent previous Windows version to roll back to.)

Monday, June 10, 2019

Comcast Xfinity routers might not select the best channel automatically

It looks like most people in my apartment complex have a WiFi router in their apartment - there's at least 30 networks in range right now. When I arrived, it was virtually impossible to connect to, much less remain connected with, my network because of all the congestion. The router in my apartment is an Xfinity, which claims to be able to select an appropriate channel automatically. However, using a WiFi inspector app on my phone, I determined that there are many strong signals in the range my network broadcast in. I picked a different channel for the 5 GHz network, which still had several other networks but with weaker signals. I disabled the 2.4 GHz network entirely since connecting to any of the available channels was hopeless. This dramatically improved connectivity: I can now use WiFi normally from anywhere within the apartment.

Sunday, June 9, 2019

Exotherm - Mercuric's rating rising

Today Mercuric once again improved its highest-achieved rating. It peaked at 1673 this afternoon, but is now at 1660. I suspect its disconnections may be from using too much memory and getting terminated, so I just tried launching it with the PYPY_GC_MAX environment variable set at 0.7 GB in order to keep away from my virtual server's capacity of 1 GB. I'll see if it's still up in the morning.

Saturday, June 8, 2019

Java's Proxy can't proxy a class, but Javassist can

One Java project I'm working on needs to be able to create instances of one class (specified dynamically) that act exactly like instances of a class with a very similar set of members. Java's built-in Proxy class can create an object that implements specified interfaces and delegates all method calls to a single handler, but since the resulting objects inherit from Proxy this works for interfaces only. However, the Javassist library provides ProxyFactory, which does something very similar but also allows setting the superclass.

Thursday, June 6, 2019

Exotherm - Established rating

Mercuric has played enough games that the FICS no longer considers its rating provisional/estimated. It's currently at 1640, moderately higher than my atomic rating! That figure is probably lower than it should be because the PyPy process has an unfortunate tendency to stop in the middle of a game (probably due to running out of memory), so it forfeits by disconnection.

Wednesday, June 5, 2019

Android library packages should be unique

I recently set up a project consisting of an Android app module and some Android libraries. I wanted the libraries to provide interchangeable versions of functionality in the app (swapped out via some other tricks), so I tried to give the libraries the same package name in their manifest as the app. However, library code appeared to then use the wrong R class at runtime - the app's, not the library's own - which was somewhat expected. Classes from the library can be dumped into the same Java package as the app classes, but the package declared in the manifest should be different. Library code can still use R (with an import).

Monday, June 3, 2019

The maven-publish plugin is incompatible with Gradle 5.1

Today I published a Java/Kotlin project to Maven Central. I did it from Gradle using the maven-publish and signing plugins using this writeup. After dealing with GPG using Bash on Ubuntu on Windows, I found that the signArchives task failed with "duplicate key" when signing the main JAR. (I previously hit the same error when trying to use the older maven plugin.) After some research I discovered that this workflow doesn't work on Gradle 5.1. Downgrading the project Gradle wrapper to 5.0 fixed the problem.

Saturday, June 1, 2019

Exotherm - Mercuric getting some games

I haven't yet set up a watchdog for the mercuric Python process on my server, so I've just been periodically checking whether it's online and restarting it if it disconnected. Despite the poor uptime, a few people have seen its seeks and played it. Its rating is currently 1568, higher than I expected!

Thursday, May 30, 2019

Exotherm - Mercuric

This morning I got emails saying that a FICS computer account had been created for me. It's named "mercuric", my second choice because "exotherm" wasn't available. (It appears available to me, and I've logged in as guest with it even today, but maybe there's something else going on.) I added the ability to post seeks and set appropriate variables, then uploaded it to my server. It can successfully log in and play games, though it eventually loses connection - I'll need to set up some way to automatically restart it when needed.

Wednesday, May 29, 2019

Exotherm - Evaluator tweaks

I recently adjusted Exotherm's position evaluator a little more. To help it avoid losing in preventable ways in the endgame, I made it value check by rook or queen, which tend to lead to explosions. To encourage it to develop more pieces in the opening, I made it slightly value the possibility of targeting more open squares. I also adjusted the pawn balance evaluator to be less likely to needlessly sacrifice a rook for a pawn.

There's still no updates on FICS's provisioning of a computer account for Exotherm.

Tuesday, May 28, 2019

Exotherm - Memory conservation

The virtual server that I'll hopefully be able to run Exotherm on has only 1 GB of RAM. I've noticed that Exotherm uses a sizable fraction of that, so if I want my server to be able to continue doing more important things, I'll need to be careful about memory usage. I noticed that the "think" module keeps board objects for all positions in the tree even after they've been evaluated, stopping the GC from collecting them. So I adjusted it to set them to None when no longer needed, allowing the GC to make progress even in the middle of a think if necessary.

Monday, May 27, 2019

Exotherm - Preparing for deployment

My previous attempt at making Exotherm note positions that led to loss worked poorly because the information was rediscovered a half move too late. Today I added similar logic to the part of the bot that relays the decided move to the server. If the evaluator found that the bot is doomed, the outer code records that the previous move the bot made is bad. This measure has no depth limit because it will only add at most one entry per game, so I'm not too worried about storage space.

I also made the bot willing to accept draws in some circumstances. It now keeps track of how many moves have occurred since an explosion or pawn move. When its opponent makes a draw offer, it will accept if no progress has been made for several moves and there is little material still existing.

These are the last changes I wanted to make to Exotherm, so after testing them I sent in my application for a computer account. They've approved the deployment of my engine - now I'm waiting for the username to be approved and the account provisioned.

Sunday, May 26, 2019

Exotherm - Hardcoded responses

After some testing today I determined that Exotherm's new ability to record the moves that led to unexpected certain doom is not working as well as I hoped. It seems like the doom is detected a half-move too late in the very common 1. Nf3 trap. It looks like I would need something more sophisticated for Exotherm to make any progress in the general case. As a temporary measure, I added a configuration section for "hardcoded" responses to recognized positions. Currently I'm only manually configuring 1. ...f6 as the response to the knight threat, but once Exotherm is deployed I would monitor for other repeatable traps.

Saturday, May 25, 2019

Microsoft deprecated the File History API

I was looking back at the File History API that I had worked with previously and noticed that the new header files are saying it's deprecated. FhCfg.h has instructions like #pragma deprecated(IFhConfigMgr) and functions in FhSvcCtl.h are have notes like:
__declspec(deprecated("FhServiceOpenPipe is deprecated and might not work on all platforms. For more info, see MSDN."))

I'm looking at header files from the SDK for Windows 10 build 17134 (version 1803), while the version from build 10240 (version 1507, the original Windows 10) does not have the notices. Weirdly, there does not seem to be any mention of this deprecation in the documentation of those functions nor anywhere on MSDN that I could find by searching.

Friday, May 24, 2019

The Exotherm draw problem

I'm hoping to deploy Exotherm to the FICS relatively soon. There is a problem I'd like to address first, though. It plays poorly in the endgame and usually thinks it's doing much better than it is. It walks various pieces all around the board without making any progress. I would like to have it accept draws if it detects no progress has been made after some time. Playing it would be much more enjoyable if one didn't have to go all the way to draw by repetition or 50 moves when the bot can't figure out what to do.

Thursday, May 23, 2019

Exotherm - Learning

Previously Exotherm tended to fall for opening traps because a move looked good to it but actually led to certain doom deeper in the tree. Before releasing it, I wanted to make it avoid at least the most common mistakes. Today I added some code to the evaluator that uses and maintains a list of positions known to be won/lost. These notes are limited to a certain depth from the start of the game to avoid clogging up the list with deep positions that will never be seen again. The main bot code saves the list as part of the configuration file.

Wednesday, May 22, 2019

Listening for HTTP requests in PowerShell

I recently needed to test that an application made a reasonable HTTP request, so I wanted a convenient way to listen for requests and see the contents. It turns out the .NET Framework has an HttpListener class, so PowerShell can do the job. This sets up a listener on port 8080, waits for a connection, and prints the body:

$listener = [System.Net.HttpListener]::new()
$listener.Prefixes.Add('http://localhost:8080/')
$listener.Start()
$ctx = $listener.GetContext() # waits for connection
[System.IO.StreamReader]::new($ctx.Request.InputStream).ReadToEnd()
$listener.Close()

Tuesday, May 21, 2019

If a class mysteriously doesn't match, consider the loader

Recently I needed to load some Java class files from disk and see if they had certain annotations. I successfully got the classes loaded, but getAnnotation never found an annotation of my specified type. Confusingly, the full list provided by getAnnotations was of the expected size and the annotation class names were as expected, but the classes didn't compare equal to the target class. Eventually I realized the problem: the annotation class was loaded again from disk, and that copy was distinct from the one loaded into my application because they came from different classloaders. The solution was to prevent the duplicate load by providing a parent classloader (e.g. the current class's loader) to the URLClassLoader.

Saturday, May 18, 2019

Launching Internet Explorer without quotes mangling

One user was employing some batch commands to launch Internet Explorer to a specified URL. They found that this worked fine the first launch, but if the strategy was used again while the first IE instance was still running, the second instance misinterpreted quotes in the command line as part of the URL and failed to load the page.

This can be worked around by preventing Internet Explorer from "merging" frame processes and sessions. Supplying -nosessionmerging -noframemerging on the command line allowed multiple instances to be launched without mangled quotes breaking the URL.

Friday, May 17, 2019

Policy Plus - Similar language ADML loading

A Policy Plus issue was filed today suggesting a change to the ADML loading procedure. Previously it tried to find an ADML in the user's display language but fell back to English (en-US) if needed. However, some languages have several dialects that are of course more similar to each other than to US English. So I made the loader look for ADML files in directories with the same language (e.g. de) before falling all the way back to en-US.

Thursday, May 16, 2019

Getting the real current screen resolution from the command line

Getting the real current screen resolution, as shown in the display settings and as desired by this Super User question, is surprisingly tricky to do programmatically. Some ways get the maximum resolution supported by the display and others produce wrong results on high-DPI screens. The most reliable way seems to be GetDeviceCaps. In SprintDLL:
call user32.dll!GetDC /return native /into hdc (native 0)
call gdi32.dll!GetDeviceCaps /return int (slotdata hdc, int 118)
call gdi32.dll!GetDeviceCaps /return int (slotdata hdc, int 117)

The first call returns the width, the second gets the height. Or in PowerShell:
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class PInvoke {
    [DllImport("user32.dll")] public static extern IntPtr GetDC(IntPtr hwnd);
    [DllImport("gdi32.dll")] public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
}
"@
$hdc = [PInvoke]::GetDC([IntPtr]::Zero)
[PInvoke]::GetDeviceCaps($hdc, 118) # width
[PInvoke]::GetDeviceCaps($hdc, 117) # height

Wednesday, May 15, 2019

The Robolectric JUnit runner doesn't support @ClassRule

I noticed yesterday that JUnit test suites run under Robolectric using @RunWith(RobolectricTestRunner.class) do not execute rules annotated with @ClassRule. I was hoping to allow some functionality to be packaged up into a rule so I could cleanly use it in many projects, but Robolectric just doesn't support class rules yet. Instead I'm creating an abstract class with @BeforeClass and @AfterClass methods, then making test suite classes inherit from that.

Tuesday, May 14, 2019

Extending the SprintDLL File History example

A while back I showed in a Super User answer how to use my utility SprintDLL to configure File History from script. I just noticed a comment on this blog from someone who would like to do something similar but with different File History settings. (The comment was posted several months ago - sorry for the long wait!) I don't recall off the top of my head how I figured out all the parts of that script, and it could be neat to show how to put this together, so I'm writing this live as I investigate. We'll see how this goes.

First we'll need a test environment that can be easily reverted. I'm using a Hyper-V virtual machine running Windows 10 Home. While I wait for it to do all the Windows updates, I create the FileHistory$ share that the current script assumes, download SprintDLL v1.2, and copy the existing script from my old answer into a file. I also copy the same script into a text editor on my real machine in case we need to revert the VM.

After a little Windows Update troubleshooting and a lot of waiting, the VM is all updated. I take a checkpoint. To make sure we have a solid starting point, I try the existing script:

SprintDLL run filehistory.sprint

Sure enough, it enables File History, setting it to keep backups until space is needed and make them at the default rate of once per hour. Our goal now is to save copies every three hours and keep them for three months.

Previously I used SetLocalPolicy to set the retention policy, so let's look there again now. It's not very specific, so let's click through to the FH_LOCAL_POLICY_TYPE enum's documentation. It has one value to set the frequency, one for the "retention type", and one for the retention age. Clicking through to FH_RETENTION_TYPES, we find one value that never deletes old backups (the default, to keep "forever"), one to keep until space is needed (what my previous script used), and one that's "age based." The FH_LOCAL_POLICY_TYPE documentation also shows what the "numeric parameter" (second argument to SetLocalPolicy) means for each policy type: time in seconds for frequency, time in days for retention. So it looks like we need to make these calls:

fh->SetLocalPolicy(FH_RETENTION_TYPE, FH_RETENTION_AGE_BASED)
fh->SetLocalPolicy(FH_FREQUENCY, 10800)
fh->SetLocalPolicy(FH_RETENTION_AGE, 90)

Here's the most similar call from my old script:
// fh->SetLocalPolicy(FH_RETENTION_TYPE, FH_RETENTION_UNLIMITED)
newslot native setLocalPolicy
copyslot setLocalPolicy = vtbl field 9
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 1, int 1)

Only the last one needs to be copied/changed - the previous two just get the function address and the first is a comment. FH_RETENTION_AGE_BASED is right after FH_RETENTION_UNLIMITED in the enum documentation, so I'm going to guess we should change that last 1 to a 2:
// fh->SetLocalPolicy(FH_RETENTION_TYPE, FH_RETENTION_AGE_BASED)
newslot native setLocalPolicy
copyslot setLocalPolicy = vtbl field 9
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 1, int 2)

FH_FREQUENCY is the very first entry in its enum documentation, so it's probably value 0. (If that turns out to be wrong, we could probably dig up some H file from the SDK to look it up.) We already have the function address, so after the existing call we just need another call and a helpful comment:
// fh->SetLocalPolicy(FH_FREQUENCY, 10800)
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 0, int 10800)

FH_RETENTION_AGE is right after FH_RETENTION_TYPE, so it's probably value 2.
// fh->SetLocalPolicy(FH_RETENTION_AGE, 90)
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 2, int 90)

We're ready to test! I copy the script into the VM and run it:

Function at 140706149603792 returned 0
Function at 140706149608592 returned 0
Function at 140706149607024 returned 0
Function at 140706149607024 returned 0
Function at 140706149607024 returned 0
Function at 140706149607520 returned 0
Function at 140706149604096 returned 0
FhServiceOpenPipe returned 0
FhServiceReloadConfiguration returned 0
FhServiceClosePipe returned 0

Promising! Let's check out the File History control panel:


Hmm, File History was enabled and we got our frequency set, but not the retention time. Out of curiosity I check an XML configuration file in %localappdata%\Microsoft\Windows\FileHistory\Configuration (mentioned in the Super User question I answered), and it does seem to have received the setting:


I wonder if my guess of 30 days per month doesn't match Windows's idea, causing it to fail to match any of the entries in the "keep saved versions" dropdown and thereby show the default in the UI. I try setting it to "3 months" using the UI and checking the configuration file again. Interestingly, the <MinimumRetentionAge> value has now changed to 3. Maybe the documentation is wrong/outdated? I revert the VM and make a small change to the script:
// fh->SetLocalPolicy(FH_RETENTION_AGE, 3)
call funcat setLocalPolicy /call thiscall /return uint (slotdata fhPtr, int 2, int 3)

The output in the console after running the script is the same. Let's see what the control panel looks like now:


There we go! Apparently the retention age parameter is specified in months, not days.

The full, corrected script is available in a Gist.

Monday, May 13, 2019

runtimeClasspath doesn't exist on Android source sets

Recently I was trying to adapt a Gradle/IntelliJ-based testing tool to an Android app module. It used sourceSets.main.runtimeClasspath and the test counterpart to assemble the classpath, but that doesn't seem to exist for Android, even using android.sourceSets instead of just sourceSets. Getting the full classpath from script seems to be tough, but in this case I really only needed my test suites to be included. So I just specified the path to those:

"${project.getRootDir()}/app/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes"

Saturday, May 4, 2019

Adding line spacing to TextView in the presence of BackgroundColorSpan

One of my Android app's screens involves TextViews displaying text formatted with BackgroundColorSpan. That one view displays multiple lines by separating rows with newline characters. I found that the background color from one line came into contact with that of the next, an effect which looked poor. Adding line spacing in the layout XML made this worse - the background color took up the spacing and was therefore different between outer and middle lines.

I ended up fixing it by using a newline-space-newline sequence instead of just a newline to separate rows. That entire sequence of three characters is formatted with a RelativeSizeSpan to be a fifth the height of the main font. (Excluding either newline from the span makes the blank line full-height.) For padding, I also include the full sequence at the start of the string and a two-character newline-space sequence at the end.

Sunday, April 21, 2019

Going back to an old Gradle version in Android Studio

Android Studio 3.4 comes with a major Gradle update (from Gradle 4.x to Gradle 5.1.1). When opening old projects, it recommends applying the Gradle update to them, but not all projects are ready for it. The File | Project Structure dialog can be used to change a project back by setting the Android Gradle Plugin Version to 3.3.2 and the Gradle version to 4.10.1. After sync, Android Studio will prompt to update once more, but it can be told to not ask again for the project.

Sunday, April 14, 2019

RichTextBox should be empty before setting tabs

I've been trying to get the Windows Forms RichTextBox control to use specified tab stops for alignment purposes. Sometimes my application needs to update these while there's text in the box. As the name suggests, SelectionTabs affects the tab stops in the current selection, so all text would need to be selected first before changing it. Even that's given me trouble, though. My current strategy is to clear the text box using ResetText, setting SelectionTabs, putting the text back, and restoring the SelectionStart.

Thursday, March 28, 2019

Fixing RichTextBox's ContentsResized boundaries with NBSPs

Previous I noticed that RichTextBox does not count trailing spaces as part of the contents when determining the needed size for the ContentsResized event. It does, however, count non-breaking spaces (NBSPs), which look identical to normal spaces. When the text in the text box is changed, I check whether the text contains a space, and if so, replace them all with NBSPs (character U+00A0). The selection/position needs to be saved before altering the text and restored afterward, otherwise the cursor will jump to the beginning. When using the text for other purposes, the NBSPs are first replaced with normal spaces.

The ContentsResized event is more accurate than GetPreferredSize

I'm trying to make a RichTextBox in a Windows Forms application grow and shrink to exactly contain its text. At first I tried TextRenderer.MeasureText, but it gave somewhat larger measurements than the text actually took. Calling GetPreferredSize on the control produced similarly wrong sizes. A Stack Overflow answer suggested listening for the ContentsResized event and using the provided rectangle. That indeed works after disabling WordWrap and scrollbars on the text box. The only downfall is that this measurer doesn't count trailing spaces in the rectangle, so as the user types a new word at the end the text jitters a bit.

Thursday, March 21, 2019

Testing SharedPreferences with Robolectric

I was looking for a way to simulate SharedPreferences for a Robolectric-tested application. It seems that the suggested way to do this involves AndroidX, a separate testing framework, which needs to be added as a dependency:

testImplementation 'androidx.test:core:1.1.0'

Then the application context can be retrieved with ApplicationProvider.getApplicationContext(), and a SharedPreferences can be obtained from getSharedPreferences as usual.

Wednesday, March 20, 2019

AutoScrollPosition coordinates need to be negated when restored

One of my Windows Forms applications has a Panel that auto-scrolls to contain many dynamically inserted controls. Sometimes its controls are cleared and regenerated. I want that to be seamless for the user, so the scroll position should be preserved. I tried recording the Value of HorizontalScroll and VerticalScroll and restoring those afterward, and that partially worked but didn't render the scrollbars in the correct place. I also tried saving/restoring AutoScrollPosition but that just left the scroll view in the upper left. I eventually found a Microsoft forum thread somewhere that said AutoScrollPosition is the property to use in this case, but the point set after the reset must be the negation (both X and Y coordinates made negative) of the original point. I'm not sure why that works, but it does.

Sunday, March 17, 2019

When Robolectric tests crash with "window creation failed"

Some students found that Robolectric tests on their Android project crashed with "window creation failed." This seems to be caused by trying to use findViewById before the view tree is ready. Most likely that's going on in an initializer on the same line as an instance variable declaration in an activity class.

Making Android Studio forget Git credentials

If the credentials used for Git are changed and Android Studio (or IntelliJ) has the old ones saved, Git operations will fail. If the repository is private, the error message may say that the repository doesn't exist. To fix this, the old Git credentials need to be removed. On Windows, this is done in the Credential Manager section of Control Panel. Once the Git-related "generic credential" entries are removed, Android Studio should re-prompt for credentials on the next repository operation.

Tuesday, March 12, 2019

Robolectric's spaces-in-profile-path bug is getting fixed

I previous reported a bug in Robolectric, that tests would fail to start if the path to the user profile directory contained a space. Today I received a notification about a GitHub comment stating that the issue has been fixed. The change will be released in version 4.2.1!

Sunday, March 10, 2019

MouseEventArgs coordinates are relative to the visible region

Today I did some tinkering involving a Windows Forms Panel that can be scrolled. To allow dragging things rendered on it, I had a MouseMove handler that used the coordinates given in the event arguments to figure out what and where to drag. I found that the drag-and-drop feature developed bizarre behavior as soon as the panel was scrolled away from the origin. Apparently, the coordinate in the event arguments are relative to the upper-left of the visible region of the panel, not the entire panel area.

Saturday, March 9, 2019

Unique constraints in the database guard against excessive insertions from the application

I'd been wondering for a while why my app was seeming slower than it used to be. Today I was looking around its database for unrelated reasons when I noticed that one table that was used by every request had ~170,000 rows, the vast majority of which were duplicates. It looks like the application has concurrency issues that sometimes led to it inserting another row inappropriately. The relevant column of the table was missing an index, so the query really hurt performance as the size grew. And as queries took longer, it looks like the concurrency problem was exacerbated, compounding the issue. I purged the extra entries and added a unique constraint. The app is now much faster!

Tuesday, March 5, 2019

When Android Studio starts treating Java files as plain text

Several students have somehow accidentally made Android Studio start treating specific Java source files as plain text. This can be fixed by removing the association:

  1. Open the Android Studio settings, File | Settings (or Android Studio | Preferences on Mac)
  2. Go to the Editor | File Types section
  3. Select Text in the Recognized File Types pane (top)
  4. In the Registered Patters pane (bottom), select and remove any entries ending in .java

Thursday, February 21, 2019

Mapping constants to names in GTKWave

GTKWave (a circuit wave viewing tool) displays multi-wire signals as numbers by default. If those signals correspond to constants for control (e.g. instruction operation codes), it may be useful to display human-readable, human-assigned names for them instead of numbers. GTKWave can do this with Translate Filter Files.

Such a file contains one mapping per line: the number (in any base you like - you'll set this later), a tab, and the desired name. Then in GTKWave, right-click the signal in the Signals pane and choose Data Format | Translate Filter File | Enable and Select. Click the "Add Filter to List" button to open a file, then select its new entry in the top list and click OK. The  lookup seems to operate on the string displayed instead of the numeric value, so set the data format again to whatever base you used in the translate filter file. (This preserves the file selection.)