You don't need casting, because you don't actually need access to AndroidImage internals: you need a more complete separation between the generic and Android-specific parts of your code. Only Android-specific classes should be allowed to have variables with Android-specific types, while generic code like an uploadImage() function should be restricted to using Image, GIContext, etc. You probably need to add something to the abstract interfaces like Image.
The sort of situation where I would use dynamic_cast is where I'm using abstract classes for portability. For example I might have an abstract class called Image and a GlContext class with a pure virtual method uploadImage(Image *), and subclasses with implementations for SDL2 and Android (not using SDL2 for Android is another story). In the Android build all Image objects would be DroidImage and GlContext would always be AndroidGlContext. The implementation of uploadImage needs access to members of AndroidImage which aren't available in the abstract base, so it has to cast.
I've implemented it in a base class Connectable, with derived classes Room and Region. The vectors of neighbours are of type std::vector. However, both Room and Region also need to do other Room-specific and Region-specific things with their neighbours, and it's better to reuse the vectors available in the base class and cast the members from Connectable to Rom or Region as appropriate than to maintain separate vectors of the subclass.
This is just a bad design; a Connectable interface doesn't make sense, because you are always connecting regions, which are aggregates of rooms, and some regions are allowed more than one outgoing door.