Differentiate between running in Sim vs Actual Device

I want to be able to differentiate between the app is running in the simulator vs the actual device. This is so that I can inject some random values into the stream since in the simulator, for WorkoutSteps, there's no current way to do simulate data (like HR / Cadence and the like)

Has anyone used the Activity.WorkoutStep details available in 3.2?

  • There used to be some undocumented hacks in the CIQ API which have gone away.

    But:

    1) You could use the :debug and :release annotations .

    https://developer.garmin.com/connect-iq/monkey-c/annotations/

    :debug
    Code blocks decorated with this annotation will not be included in release builds at compile time.

    :release
    Code blocks decorated with this annotation will not be included in debug builds at compile time.

    2) You could try to run some CPU-intensive code and compare the elapsed time:
    https://forums.garmin.com/developer/connect-iq/f/discussion/5113/how-to-detect-i-m-not-running-in-the-simulator

    (I have not tried this.)

    3) You could try to find code which crashes in real device but not sim. (Note that the suggestion in this thread does not crash for me. As the person who suggested it pointed out, you would have to keep this function up to date in case the crash is resolved in the future.)

    https://forums.garmin.com/developer/connect-iq/f/discussion/3761/test-if-running-on-simulator

    4) In the same thread ^, it's suggested to use your own annotation (passed in with "-x" on the command line.) Seems almost equivalent to using the built-in debug and release annotations. Using the built-in annotations would be better in the sense that you don't have to remember to remove/change your custom annotation on the command line before you release.

    5) You could just hardcode your own isSim global boolean variable and temporarily set it to true. Same issue as 4) (you can forget to undo it.)

  • Right now I'm using a variant of the isSim boolean and yes, I've forgotten to turn it off at times too.

    I'll check out the debug / release annotation. thanks

  • You're talking about writing a bunch of code that generates data... you'll want to compile that out, right? If so, you're going to need to use an exclude annotation or alternate source path to remove all of that code from your app anyway. The debug/release annotations are good for this, but may not work for all needs.

    If you created a build script to do your release builds (the .iq file builds), you could just use a different jungle file that builds with the simulator-specific code excluded..

    # monkey.jungle
    project.manifest = manifest.xml
    
    base.excludeAnnotations = $(base.excludeAnnotations);not_simulator_build
    
     

    # release_monkey.jungle
    project.manifest = manifest.xml
    
    base.excludeAnnotations = $(base.excludeAnnotations);simulator_build
    

    Your build script could just reference this alternate jungle file..

    # makefile
    
    ship:
        java -Dfile.encoding=UTF-8 -jar $(CIQ_SDK)/bin/monkeybrains.jar -e -f release_monkey.jungle -o bin/WebRequest.iq -w -y ./developer.key
    

    You'd have to remember to use the release build script, but that isn't really all that bad if you are building your release builds using automation (jenkins/travisci/...).

    If you really just need to tell if you're running in the simulator or not, you should be able to do so using the uniqueIdentifier field on device configurations that support it. This field will return the same value for your app in the simulator regardless of which device you are simulating.

    While it isn't a problem for your use case, this won't work if the target doesn't have at least CIQ 2.4.1 support. You could implement a fallback using the UserProfile data to see if it is the hard-coded values that are always returned by the simulator. There is a small chance for a collision with an actual user and it would require the User Profile permission.

    /* global */ var cached_is_running_in_simulator = null;
    
    function is_running_in_simulator() {
    	if (cached_is_running_in_simulator != null) {
    		return cached_is_running_in_simulator;
    	}
    	
    	var deviceSettings = System.getDeviceSettings();
    	if (deviceSettings != null) {
    	
    		if (deviceSettings has :uniqueIdentifier) {
    			var uniqueIdentifier = deviceSettings.uniqueIdentifier;
    			// System.println(uniqueIdentifier);
    			
    			// this is checking against the uniqueIdentifier for my app
    			// it will need to be changed to work with your app
    			cached_is_running_in_simulator = "e9e4e75f045a5c90eb091793acfa52fae5f367af".equals(uniqueIdentifier);
    		} else {
    			
    			// assume we have UserProfile permission.. ew.
    			var userProfile = UserProfile.getProfile();
    
    			// fall back to user profile data which is hard-coded for the simulator
    			cached_is_running_in_simulator = (userProfile.activityClass == 75);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.birthYear == 1980);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.gender == UserProfile.GENDER_FEMALE);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.height == 182);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.restingHeartRate == 60);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.runningStepLength == 1200);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.sleepTime.value() == 79200);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.wakeTime.value() == 28800);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.walkingStepLength == 900);
    			cached_is_running_in_simulator = cached_is_running_in_simulator && (userProfile.weight == 68000);
    		}
    	}
    	
    	return cached_is_running_in_simulator;
    }

  • Travis, interesting.  If I need to do this, great ideas!

    ------------------------------------------

    One thing with using (:release) and (:debug), is I know for me, when I build a sideload, I do a debug build (so you get a nice stack trace if it crashes).  And if it's running on a real watch, you want real data, not simulated stuff.

  • Yes. The debug vs release assumes that you're using debug builds in the sim and release builds for device and production. This is now things are done by default, but it isn't how it is always done.

  • Can we differentiate between the app is sideloading in an actual device or downloaded from the app store?

  • Within the app I'm pretty sure you can't.

    From the Connect IQ you'll not see the logo and the description next to apps that are ONLY* sideloaded.
    * ) ONLY means that if you compile a release build (maybe even debug?) with the same id in the manifest and you sign it with the same signature as the one in the store, then even this won't work. Actually you can do this:
    1. install version 1 from the store
    2. side load version 2 - still will see it in Connect IQ
    3. when there's version 3 released it'll automatically upgrade to it.

  • Today, I'd go with the release vs debug annotation mentioned earlier in this thread.  When you run in the sim, it's always a debug build.  When you build an iq file, it's always a release build.  When building a sideload, it can be either and simple to change.