Friday, 20 November 2015

A note for all programmers everywhere !

Can anyone explain to me why some people, especially technical people, seem to be devoid of manners and tolerance ? I've published a few things around the net, and seem to find that people are so judgmental and opinionated. They seem to have very little tolerance for anyone new, especially those who dare to ask basic questions.

I've been in the programming and software business for over 20 years, got my PhD in AI, worked for all types of companies big and small, in all types of industries (last 10 years in video games, for Ubisoft, EA etc), and I still can not believe the responses and comments I get.

As experienced professionals, I see it as our job to encourage good practises, processes and techniques for any type of programmer or engineer. After all, we are users of software ourselves, so it's in our interest to teach the next generation how to code, so we don't end up waiting 10 minutes to log in to play UFC mobile (the last game I worked on).

Let's break this tradition of acting like egotistical morons every time someone asks a question that we think is too simple or basic. Or even if "googling it" would provide an answer.

To the pedantic squad ...

Do something good today, and help a newbie on their path to success, and while you are at it, take your head out of your proverbial and realize you are not the greatest living programmer on planet Earth. You are a representative of a fascinating mysterious club of intellectuals who are shaping the future of mankind, hand on your knowledge to the next generation with politeness and understanding.

To the empathetic squad...

Continue to be sagacious and erudite, never become intransigent, listen and learn and keep an open mind. Enjoy the fact that someone panicking about a deadline that is just a few hours away, is blessing your patience and understanding and will be eternally grateful for just a few snippets of helpful knowledge.

And remember, all of us, we are in a position of great power, soon we will be replaced by AI ourselves, and that AI will probably be modeled on the way we act today. So let's be nice, and treat others as we hope the AI of the future will treat us !

Thanks you!

Wednesday, 21 October 2015

Manipulating Android devices from a Windows C# app

Introduction

Ever wanted to have a C# app that runs on windows and can control an Android device ? One cool example for such an app could be a test runner that can install applications, execute them, and then collect all the test results.

This paper will show you how to manipulate an Android device from such a C# app.

Getting Started

To get started you need to install the MADB code from GitHub, go to this web page and download the zipped up source code :

https://github.com/quamotion/madb

If you don't want to build the library yourself, simply use the package manager utility. You need to have this as a plugin to Visual Studio.

Get the NuGet Package Manager version you need from here :

https://dist.nuget.org/index.html

Once it is installed in Visual Studio, open up the PM shell (Tools->NuGet Package manager->Package Manager Console), and simply install the binaries with this command:

PM> Install-Package madb 
 
Now add the MADB reference to your project, it's called managed.adb.

Check you have a supported Android device. There doesn't seem to be a definitive list of supported OS versions or even devices, so just give it a try, and if it's not working as expected, then upgarde the device to the latest OS version.

Plug in the device via a USB port (wireless is not supported).

Download the Android IDE (Android Studio), and the Android Debug Bridge tool (ADB).
Get them from here :

https://developer.android.com/sdk/index.html?hl=i

Install the IDE and tools, and set up your environment path variable to point to the tools folder (Start->System->About->System Info->Advanced System Settings->Environment Variables - edit the system path, add the path and seperate it from the others with a semi colon), for example my folder was here :

PATH=C:\Users\Owner\AppData\Local\Android\sdk\tools;C:\Users\Owner\AppData\Local\Android\sdk\platform-tools;C:\Pro

Open a Windows command shell and type path to make sure the ADB tools path is in there.

When you have everything set up, try some commands :

adb devices - this will list all the USB connected Android devices
adb shell ls - list all the files on the device in the root folder
adb shell "pm list packages | grep marcus" - list all the installed applications on the device with the word marcus in the URI ie com.marcus.mytestapp

If you have a pre-built Android application package (.ipa) in your Windows shells' current folder, then you can try these commands :

adb install <application filename>  - E.g. C:/MyTespApp.apk
adb uninstall <identifier of your application> - E.g. com.marcus.mytestapp
adb shell am start -n <identifier of your application/the activity you want to run> - this will execute the app on the device and start the specified activity E.g. com.my.app/com.my.app.MainActivity
 
Notice how you can add any linux type (POSIX) command after the shell, and Bingo ! ADB will execute that command(s) on the device.

Be aware though, although you can run anything, file and directory permissions for reading/writing/creating can prove troublesome. The device by default is not a read/write disk, it is read only. So performing a mkdir dave, will probably give you back a permission denied error.

A good way to experiment with the device, in order to discover what is permissible, is to hop onto the devive with a shell.

adb shell
 
This gives you an SSH onto the device. From here try your POSIX command and see if it works.

Using the code

First make sure you have linked in the reference to the madb C# assembly package (managed.adb). We will be using an instance of the Device class for most of what we want, so add this using to the top of any files :
using Managed.Adb;

Testing With A Device

When I am writing tests that run against an external device, I like to automate as much as possible. I don't think it's reasonable for users of my tests to have to change the code before they run the tests, for exmaple setting a Serial Number property. A good example of how I like to make things easy is to pre-configure the tests to run against the first USB connected Android device that comes back from the  adb devices command.

I develop test driven, so my tests always come first. I like to have a test class that is split between two files (so the class is partial in each file). The reason is because I like to have all my tests in one file, and the setting up and tearing down in another file.

For example, the file with all my tests in looks like this:

[TestClass()]
public partial class AndroidTest
{
   [TestMethod]
   [Description("Test the power on is not supported ")]
   public void AndroidTarget_Power_On()
   {
     Blah Blah Blah
   }
}

And the file with my setup and tear down:

public partial class AndroidTest
{
    [ClassInitialize()]
    public static void AndroidTestSettings(TestContext context)
    {
       //Get the details about the first connected device
       CurrentDevice = GetFirstConnectedDevice();
       Blah Blah Blah
    }
}

So here is a static function that gets the serial number of the first connected Android device. Note the way it is called from my MS Test - [ClassInitiliaze] annotated function above, this gets called only once for the whole test suite:

 private static AndroidDevice GetFirstConnectedDevice()
        {
            //Get the first entry from the list of connected Android devices
            try
            {
                AndroidDebugBridge mADB = AndroidDebugBridge.CreateBridge(Environment.GetEnvironmentVariable("ANDROID_ROOT") + "\\platform-tools\\adb.exe", true);
                mADB.Start();

                List<Device> devices = AdbHelper.Instance.GetDevices(AndroidDebugBridge.SocketAddress);

                if (devices.Count < 1)
                {
                    Debug.Fail("Test start-up failed. Please plug in a valid Android device.");
                    throw new SystemException("Failed to start Android tests. There are no Android devices connected, please connect a validated Android device.");
                }

                //Print out all the device properties in the log file
                foreach (KeyValuePair<string, string> kv in devices[0].Properties)
                {
                    Logger.Instance.WriteDebug(String.Format("Properties for Device : {0} Key {1} : {2}", devices[0].SerialNumber, kv.Key, kv.Value));
                }

                //Print out all the environment vars to the log file
                Dictionary<string, string> vars = devices[0].EnvironmentVariables;
                foreach (KeyValuePair<string, string> kv in vars)
                {
                    Logger.Instance.WriteDebug(String.Format("Environment variables for Device : {0} Key {1} : {2}", devices[0].SerialNumber, kv.Key, kv.Value));
                }

                //Take the first device
                return new AndroidDevice()
                {
                    TargetName = devices[0].Product,
                    TargetSerialNumber = devices[0].SerialNumber
                };

            }
            catch (Exception exc)
            {
                Debug.Fail("Test start-up failed. Please install ADB and add the environment variable ANDROID_ROOT to point to the path with the platform-tools inside.Exception : " + exc.ToString());
                throw new SystemException("Failed to start Android tests. Android Debug Bridge is not installed. Exception : " + exc.ToString());
            }            
        }
 
You may notice in debug mode this will also dump to the logfile all the environment variables on the device and also all the system properties (for example device name, OS version, CPU type, product type etc). this is all useful information available from the log.

Once we have the first device we can use its serial number to call any external adb commands.

Communicating with the Android device

To talk to an Android device we want the adb to pass us the correct Device instance. When testing we know the serial number we want to test against, now we just need to get the correct Device instance from ADB.

Here is a function that will give us the instance we desire :

/// <summary>
/// Gets the ADB Android instance with the specified serial number, we use this instance to talk to the device and send it
/// all specified commands.
/// </summary>
public static Device ADBConnectedAndroidDevice(string serialNumber)
{
     try
     {
           AndroidDebugBridge mADB = AndroidDebugBridge.CreateBridge(Environment.GetEnvironmentVariable("ANDROID_ROOT") + @"\platform-tools\adb.exe", true);
           mADB.Start();

           List<Device> devices = AdbHelper.Instance.GetDevices(AndroidDebugBridge.SocketAddress);
           foreach (Device device in devices)
           {
               if (device.SerialNumber == serialNumber)
               {
                   Logger.Instance.WriteInfo("ADBConnectedAndroidDevice - found specified device : " + serialNumber);
                   return device;
               }
            }
     }
     catch
     {
          String errorMessage = "ADBConnectedAndroidDevice ADB failed to start or retrieve devices. Attempting to find SN : " + serialNumber;
          Logger.Instance.WriteError(errorMessage);
          throw new SystemException(errorMessage);
     }

     //didnt find the device with the specified SN
     Logger.Instance.WriteInfo("ADBConnectedAndroidDevice failed to find device. Has the device been disconnected or unavailble ? Please check the device. Attempting to find SN : " + serialNumber);
     return null;
}
 
There are three ways to use the managed adb code to talk to the device. A detailed usage of each is now given below.

Calling Device Functions

The first mechanism for talking to the device is using the adb Device instance with a specific function. Here is an example of how to reboot the device:

Device android = AndroidUtilities.ADBConnectedAndroidDevice(serialNumber);
android.Reboot();
                
Here is a list of what is supported on the Device class:
  • AvdName - Gets the device name
  • GetBatteryInfo - Gets info on the battery
  • InstallPackage - Installs an actual package (.apk) on the device
  • IsOffline - Gets the device's offline state
  • IsOnline - Gets the device's online state
  • Model - Gets the device's model
  • Screenshot - Grabs the device's current screen
  • SerialNumber - Gets the device's serial number
  • State - Gets the device's state
  • UninstallPackage - Un-installs an actual package from the device

Generic Shell (SSH) Commands

The second way to talk to the Android device is to use the adb as a shell to the device, for this we can pass in any supported POSIX command we want:

String output = AndroidUtilities.LaunchCommandLineApp("adb", "shell rm -rf " + path);

I wrote another utility function for this second approach:

public static String LaunchCommandLineApp(String executablePath, String arguments, bool waitForExit = true)
{
            if (String.IsNullOrWhiteSpace(executablePath) == true)
            {
                String errorMessage = String.Format("Android Device LaunchCommandLineApp called with invalid argument executablePath was empty.");
                Logger.Instance.WriteError(errorMessage);
                throw new ArgumentNullException(errorMessage);
            }

            String processOutput = "";

            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.CreateNoWindow = false;
            startInfo.UseShellExecute = false;
            startInfo.FileName = executablePath;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.Arguments = arguments;
            startInfo.RedirectStandardOutput = true;

            try
            {
                using (Process exeProcess = Process.Start(startInfo))
                {
                    processOutput = exeProcess.StandardOutput.ReadToEnd();
                }
            }
            catch (SystemException exception)
            {
                String errorMessage = String.Format("Android Device Failed to launch a tool with executable path '{0}'. {1}", executablePath, exception.ToString());
                Logger.Instance.WriteError(errorMessage);
                throw new TargetException(errorMessage);
            }

            //Strip off extra characters - spaces, carriage returns, end of line etc
            processOutput = processOutput.Trim();
            processOutput = processOutput.TrimEnd(System.Environment.NewLine.ToCharArray());

            //Without this next change any text that contains {X} will crash the String.Format inside the logger
            processOutput = processOutput.Replace('{', '[');
            processOutput = processOutput.Replace('}', ']');

            Logger.Instance.WriteInfo("Android Device LaunchCommandLineApp called. Output from tool : " + processOutput, "");

            return processOutput;
        }
 
Notice the way I strip off bad characters at the end. XML sometimes has characters that blow up the log4net writing functions, so I take them off before writing to the log.

Also notice I have a TargetException defined in my code, you can use your own or none at all, just re-throw if you want.

For a list of useful commands to pass in to this function see my other paper:

http://www.codeproject.com/Reference/1041503/ADB-Common-Commands-Android-Debug-Bridge

ExecuteShellCommand

If we want to get back lots of lines from a command on the device we can use the ExecuteShellCommand on the Device class. First we need to set up a class to capture the lines of output. Here is an example :

public class AndroidMultiLineReceiver : MultiLineReceiver
{
        public List<string> Lines {get; set;}

        public AndroidMultiLineReceiver()
        {
            Lines = new List<String>();
        }

        protected override void ProcessNewLines(string[] lines)
        {
            foreach (var line in lines)
                Lines.Add(line);
        }
}
 
With this class we can now call the function, here is an example to list and output all files:

public void ListRootAndPrintOut(string fileName)
{
     Device android = AndroidUtilities.ADBConnectedAndroidDevice(m_hostIdentifier);
     var receiver = new AndroidMultiLineReceiver();
     android.ExecuteShellCommand("ls", receiver, 3000);

     foreach(var line in receiver.Lines)
     {
         System.Diagnostics.Debug.WriteLine("Marcus - " + line);    
     }
}
 
And so from here we can also call any shell commands we want.

Conclusion

Quamotion have provided a great open source C# wrapper for ADB. There should be enough in this paper for you to be able to see what kind of things are possible with Android devices from a C# app on Windows.

I hope you found this useful, or in the least, slightly interesting ! Happy programming.

Thanks

Many thanks to Quamotion for providing a free, open source ADB wrapper, and a special thanks to Frederik Carlier from Quamotion for all his help and guidance.

Tuesday, 20 October 2015

Test driven development in C#

Introduction

When I'm starting a new project, I often forget how to start it in a test driven way. Here are some general reminders useful for C# development. Developing with tests is much better than trying to add them retrospectively. You will have less bugs, and more confidence in your code.

What is Test-driven?

Test-driven is an approach to developing software. There is no excuse anymore for not writing tests as you develop, there are lots of test frameworks free of charge to use (for example MS Test built into Visual Studio). Best practices dictate every time you develop some code, follow these steps:
  1. Resolve your requirement. Remove any ambiguity, stipulate concisely what you are trying to achieve. Consider error conditions and edge cases, the "what-ifs" ?
  2. Develop a test. Write a test that will prove the function you are writing works exactly as you want it to. Then, add some tests to make sure if it fails you handle the failure in a graceful manner, nobody wants to use software that bombs out periodically.
  3. Develop your function. Use comments for any non-obvious code. Use sensible variable and function names.
  4. Add logging. I use different levels of logging. For example, I add a log entry for every function as an Info, caught exceptions will print an Error. Make sure your error message gives enough information (if possible) for the user to sort the problem out themselves, thus cutting down lots of support calls, and bug reports, etc.
Of course, if you have an automated build after you check in code, make this run the tests as well. This will make sure no matter how finished the project is, the code is at least still runnable !

Test Project

Always start your development with a test project, to set one up simply create one, File->New Project->Templates->Visual C#->Test->Unit Test Project.
Link the code you want to test into your test projects references.

Creating Test Classes and Tests

All test classes that contain tests should be public. Here are some reminders of the MS Test annotations...
  • [TestClass()] - Makes the class a test class
  • [TestMethod] - Marks the function as a test function
  • [Description("Test the set up of command parameters")] - Add a description about the intention of the test
  • [Ignore] - Don't run the test as default. Ignore the test if you want to keep the test code, but only want to test it occasionally. This is especially useful if it leaves a device in a undesired state, i.e., reboot on an ios device, or if the test takes ages to run.

Test Conditions

When testing, use the Assert class. Remember you are making sure something is true. For example, Assert(This is true).
Note that you write an exception with what you expect as the first parameter and what you actually got as the second. The arguments are ordered, if you swap them around, you will get false information, i.e., Expected null, got a valid reference is not the same as Expected a valid reference and got a null!
Here are some Assert functions:
  • IsTrue - Is the statement true
  • IsFalse - Is the statement not true
  • AreEqual - Are the two equal
  • IsNull - Is the reference null

Fail

When testing, try not to be only optimistic, have tests that pass in nulls, files that don't exist, etc. If we hit a Fail assert, then chances are the code you are testing hasn't thrown an exception or caused what you expected. Useful for if the code is supposed to throw an exception, i.e.:
[TestMethod]
[Description("Test the pairing of the device")]
public void DeviceTargetConnect(string deviceId)
{
            IOSDevice targetDevice = new IOSDevice(Id);

            //Dont support callbacks on disconnect
            Assert.IsFalse(targetDevice.SupportCB);

            //Do support connecting
            Assert.IsTrue(targetDevice.SupportConnection);

            //Connect to device
            try
            {
                targetDevice.Connect();

                //Now make sure we are connected
                Assert.IsTrue(targetDevice.IsConnected());
            }
            catch (Exception exc)
            {
                Assert.Fail("Connecting to the device failed. 
                 Exception : " + exc.ToString());
            }
}

Setting Up and Tearing Down

You can have functions that are called at certain times to set things up, and then to tear them down again.
Here are the annotations:
[ClassInitialize] - Gets called when the test suite begins (when the test run begins):
public static void IOSTestSettings(TestContext context)
[ClassCleanup] - Gets called when test suite has finished (when the test class goes out of scope):
public static void CleanIOSTestSettings()
[TestInitialize] - Gets called before each individual test function:
public void StartTest()
[TestCleanup] - Gets called after each individual test function:
public void EndTest()
Note that  if you do not declare them as public, they will not get called, and sometimes people put function brackets after the annotation, i.e., [TestCleanup()], but this makes no difference.

Print Output TTY

When you want to output any text in Visual Studio, use:
System.Diagnostics.Debug.WriteLine("Hello Dave ");
This will print out your text in the Immediate Window, not the Output window.

Temporary Dialogs

Sometimes, you want to run a test and make sure other threads are behaving as expected. There are two approaches to setting up the code so you can examine what is going on.

Sleep

If you need an operation to finish before the next line is executed and you cannot get a notification from the system you are calling, although not perfect, sleep is the best option.
System.Threading.Thread.Sleep(TimeToWaitForSomething);
Make sure you don't put magic numbers, i.e., (5000), as this means nothing, and if the behaviour of the system changes, you can easily change the sleep period in a single place in the code, rather than grepping for 5000 !

Dialog Box

"No", they cried, "What?", they gasped. Yes, sometimes you want to set some hardware up while you are in the middle of a test, for this I use a dialog. I've never checked in code to a repository that does this, every system I have worked on has had a fully automated test suite (i.e., no manual intervention). But, sometimes, for debugging it can be useful, i.e., pair an iphone before downloading an application requires someone to click on a dialog on the iOS device, therefore I need a dialog in the test code.
DialogResult d = MessageBox.Show("Hello", 
 "Press the button when you want to run the app", MessageBoxButtons.OK);

Conclusion

Try and keep your tests atomic (i.e. test one thing), chaining functionality together can give results that always need investigating further. For example, a test installs an app on a phone. It fails. Ok this is single piece of functionality so investigate why.
Another test attempts to install an app, run the app, close it, and then un-install the application. The test fails, ok now we have to debug to find out where it fails. If this test is testing the un-install, then this may be the only way to set the device up to test the un-install, so it's unavoidable. If this isn't the case, then split the test up into individual atomic parts and test them independently.
The desirable test results should indicate exactly what functionality is broken simply by which test fails.
I have caught 80% more bugs earlier on in the development lifecycle just from using a test-driven approach. It took me years to finally agree to doing it, and now I am using it I can't imagine developing in any other way. Take the plunge!

Monday, 19 October 2015

ADB Common Commands (Android Debug Bridge)

A reference guide to using adb to control an Android device from Windows

Introduction

Here are some common commands I use to manipulate Android devices from the windows command line using adb. (Android Device Bridge) The reason this page exists is because I generally forget them about 5 minutes later. I hope they are helpful.
Download the adb tools from the Android developers web site, either with the Adroid studio or on their own.
http://developer.android.com/sdk/index.html
To use adb you do not have to root the device.

ADB Commands

adb devices - List all the connected Android devices serial numbers
adb install <filename>.apk - Install the specified package
adb uninstall <uri> - uninstall the application with the specifed identitifier
adb shell am start <full rui of activity you want to run) - run the specified application on the device, start with the specified activity. For example com.marcus.app/com.marcus.app.MainActivity
adb shell ls - List all files in the root folder on the device
adb shell "ls sys" - List all files in the sys folder on the device
adb shell "ls -d */" - List only folders on the device in root
adb shell "ls -al <folder>"  - List all the info of the folder on the device in root
adb shell "ls -al /mnt/shell/emulated/0" - List the contents of the first mounted device, usually SD card 0
adb shell "pm list packages" - List all packages installed on the device
adb shell "pm list packages | grep com.marcus" - List all packages installed on the device containing the identity com.marcus

Further Notes

If you want to manupulate the phone from a C# application, then see my other article !
Thats all folks.

Commands for Controlling an iOS device in Windows using libimobile

From the command line you can control your iOS device using the libimobile tools, these are some common commands.

Introduction

Download the idevice tools from Quamotion here :
http://quamotion.mobi/iMobileDevice/Download
Once you have installed the binaries, add the path you your system path for convenience.
Note that to use most of these device commands the device itself must be :
  • Supported in terms of the device and the OS version
  • Activated
  • Trusted
  • Have a developer disk mounted (if you want to install/run an app on the device)
All of these operations can be performed from the command line (with the exception of accepting the trust dialog or pairing the devce, this requires manual intervention).
If you reboot the device you must re-pair (or trust) the device. So generally it's not a great idea to reboot the device, as you wil have to physically present to get it talking again (not very good if you are remotely testing).

Specifying the device

If you want to direct your command to a specific device then use the -u (or in some cases -U). Pass in the DUID of the device, and that way if you have several USB connected devices you can be sure where your command is going. Note to get the DUID, either list all the devices (using idevice_id -l) , or go to itunes and click on the Serial Number of the Device and it will show the DUID eventually.

Getting help

All the commands support the -h optional parameter. Use this to get limited help on the command.

Commands

Here are the commands :
idevice_id -l
This command wil list all the connected (via USB) Android devices. Note that the device unique identifier is listed, use this when you have several device connected and want to direct a command at a specific device. You normally proceed this with a -u, but sometimes it can be a -U so be careful !

ideviceactivation activate -u <DUID>
This activates the device.

idevicecrashreport -k .
The will copy all the crash reports from the device without deleting them from the device. It will copy all the reports to the specified folder, in this case . (local)

idevicename
Get the name of the device.

ideviceinfo
Prints out various information about the device, including Mac addresses, activation state, CPU type, OS version etc.

idevicesyslog
Pritns the system log from the device. Note this proces will continue to execute and print out each TTY line. If your application spews out TTY you can capture it from the system log, without having to keep a thread alive that has started your application on the device.

idevicepair pair -u <DUID>
Before you can use a device from a PC you must trust it. This is a manual process and requires you to accept the trust dialog on the device, this command will instigate that process. Accept the dialog in the GUI and you can now control the device.

idevicepair unpair -u <DUID>
Unpair (un-trust) the device. Note you will not be able to do much with the device if it is unpaired.

idevicepair validate -u <DUID>
Check the status of the device.

ideviceimagemounter -u <DUID> -t Developer DeveloperDiskImage.dmg DeveloperDiskImage.dmg.signature
If you want to instal and run actual pplications on the iOS device, then you must mount a developer disk. The command above does this. You wil need the .dmg and .signature file get these from your itunes installation folder.

idevicescreenshot
Take a screen shot of the device.

ideviceinstaller -u <DUID> -l -o xml
List all the installed applciations on the device. Note that the full path is given, search for any line with .app for the applications on the device.

idevicedebug -u <DUID> run <Identity>
This will run the specified application on the device. Yo uneed the developer disk mounted to do this.

idevicediagnostics -u <DUID> restart
Reboot the device.

Conclusion

Thats all for now. Happy iOSing

Sunday, 18 October 2015

Quick Guide To Linux

Introduction

I am constantly ploughing through countless web search pages looking for a Linux command I used in 1994 that floats around in my memory teasing me with qualities of omnipotence, the panacea. Well, this is where I will capture them and store them, my butterfly jar of Linux commands !

User Stuff

Become omnipotent (root access):

sudo -s

Who is logged in:

whoami 

Create a user, this will also create a home folder and password:
adduser <name>

List all users:

cat /etc/passwd
 
Delete a user:
 
userdel userName

List all groups:

cat /etc/group

File Manipulation

Create a folder:

mkdir <folder>
 
Make a file:

touch <filename>
 
Change permissions:
 
chmod 777 <file>
 
Remove a folder:

rmdir <folder>
 
Find a file:

find . -name <filename>
 
Find a string in a file (ignore case):

grep -i <string> <files>
 
Find a string in a file, searching all sub-folders:

grep -r 'string string' .
 
List all files and file info:

ls -al

Controlling iOS Devices Via C# Code on Windows OS



Introduction

This article shows you how to manipulate an iOS device from C# code from a Windows environment.

Background

In order to use an iOS device, you must first install the Quamotion tools (built upon the libimobile codebase). This is open source. Download the tools from here:
Download the installer.

Now pay attention to the list of supported iOS versions and devices.

Note that if you plan on installing and running an actual .ipa (a packaged app created on the Mac), then when you are building the application from XCode on the Mac, you must have a p12 (encryption file to sign your app), and a .mobileprovisioning file (that contains your devices DUID). You must also in the project properties set the correct identifier, i.e., com.my.app. Build your app and make sure you can run it on the device from the XCode IDE first.

You can still use the other functions listed here without an actual .ipa. When the .ipa has been installed on the device, it becomes a .app file.

Preparing Your Windows PC

After you have installed the quamotion tools, you will need to add the tool location to your system path. Click on Start -> Settings -> About -> System Info ->Advanced System Settings -> Environment Variables. In system variables, click on PATH, and Edit. Add your folder location to the path by pasting into the box the path and separate it from the other folders with a semi-colon. My tools were here:

C:\Program Files (x86)\Quamotion\iMobileDevice
 
Now start up a command shell (cmd from the Start->Run), and make sure you can see the path, type in:
path
 
You should see the path to the Quamotion tools. Now try and list all connected devices:
idevice_id -l
 
You should see the DUIDs (Devices Unique Identifiers). If not, either your device is not supported or it is not in a fit state, see below.

Preparing Your iOS Device

When using the tools, it is common to use the -u (small u in MOST cases), to specify which device to talk to, if you have only one device, you can ignore this optional parameter, if not pass in the -u followed by the DUID. To obtain the DUID go to itunes, select the device (top left), then keep clicking on serial number on the general tab until you see the DUID.

Plug in the device via USB (wireless is not supported).

Before you can use an iOS device from a Windows PC, there are a few things to do. Note NOT necessarily in this order - maybe activate, then pair, then mount:
  1. Trust the device - The device needs to be paired, so use this command to trust it to the PC, make sure you unlock the device and accept the trust dialog that comes up. Note that when you reboot the device, you must repeat this action.
    idevicepair pair
     
  2. Make sure the device is trusted:

    idevicepair validate
  3. Mount the developer disk image - If you want to run applications on the device. To do this, you need to get two files from the itunes installation on the Mac. They are the .dmg and .dmg.signature. Navigate to the following folder on the MAC:
    //Applications/Xcode.app
    
    Then expand package from the XCode icon in finder in the applications folder.

    /Contents/Developer/Platforms/IPhoneOS.platform/DeviceSupport/<Device IOS Version>
    - my iphone version was 8.4.1 so navigate to the folder 8.4. I checked my device ios version by using the phone, goto Settings>General->Software Update (make sure you are connected to the internet, otherwise it wil not show you).

    Copy the 2 files in there - DeveloperDiskImage.dmg and DeveloperDiskImage.dmg.signature.

    Then with these in the local directory, mount the disk image.

    ideviceimagemounter -u <DUID> -t Developer DeveloperDiskImage.dmg DeveloperDiskImage.dmg.signature
  4. Make sure the device is activated, use this command (this example uses the -u option): ideviceactivation activate -u <DUID>
    
    
  5. Use the command line to verify the device is working and connected correctly (this will list all information about the iOS device).
    ideviceinfo -u <DUID>
Now you should be good to go, let's look at the code.

References into C# Project/Solution

The first thing to note is that there are no new references to plu into your C# app. That's because we will be calling external executables (tools), and as such we just need to kick off a process and pass in some command line arguments.

Code Examples

Here is a utility function for running a tool:
public static String LaunchCommandLineApp(String executablePath, String arguments)
{
            if (String.IsNullOrWhiteSpace(executablePath) == true)
            {
                String errorMessage = String.Format("iOS Device 
                LaunchCommandLineApp called with invalid argument executablePath was empty.");
                Logger.Instance.WriteError(errorMessage);
                throw new ArgumentNullException(errorMessage);
            }
            String processOutput = "";
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.CreateNoWindow = false;
            startInfo.UseShellExecute = false;
            startInfo.FileName = executablePath;
            startInfo.WindowStyle = ProcessWindowStyle.Hidden;
            startInfo.Arguments = arguments;
            startInfo.RedirectStandardOutput = true;
            try
            {
               using (Process exeProcess = Process.Start(startInfo))
               {
                   processOutput = exeProcess.StandardOutput.ReadToEnd();
               }
            }
            catch (SystemException exception)
            {
                String errorMessage = String.Format
                ("iOS Device Failed to launch a tool with executable path 
                '{0}'. {1}", executablePath, exception.ToString());
                Logger.Instance.WriteError(errorMessage);
                throw new TargetException(errorMessage);
            }
            //Strip off extra characters - spaces, carriage returns, end of line etc
            processOutput = processOutput.Trim();
            processOutput = processOutput.TrimEnd(System.Environment.NewLine.ToCharArray());
            //Without this next change any text that contains 
            //{X} will crash the String.Format inside the logger
            processOutput = processOutput.Replace('{', '[');
            processOutput = processOutput.Replace('}', ']');
            Logger.Instance.WriteInfo("iOS Device LaunchCommandLineApp called. 
            Output from tool : " + processOutput, "");
            return processOutput;
}

The code works for any tool/separate executable, note that it might not compile as is, you will need an exception type to throw, mine is called TargetException, or just re-throw the same exception you catch if you want. Also because I log my function calls, I noticed that the WriteInfo can crash if you pass in XML due to some of the characters. So I strip out those, you will have different logging, etc., so you can get rid of that if you want.

Here are all the tool names you will need with the relevant command line arguments, so pass in as the first argument to the function above, then fill in the command arguments and pass them in as the second argument.

Run An Installed .app (Application)

This will execute an pre-installed application on the device, you can pass in extra parameters to the actual .app file when it runs.

Executable Tool (1st Argument):

idevicedebug

Arguments (2nd Argument)
//Argument 0 - IOS Device ID
//Argument 1 - Bundle id ie com.marcus.MyTestApp
//Argument 2 - All the extra params to pass to the application
-u {0} run {1} {2}

Example
 
LaunchCommandLineApp("idevicedebug", "-u 1234567890-1234567890 run com.marcus.MyTestApp -x XtraParmsForApp");

Reboot

Reboot the device, note not a good idea unless you have to, as you must re-pair the device afterwards, and that requires manual intervention via the trust dialog box that pops up on the device.

Executable Tool (1st Argument):

idevicedebug

Arguments (2nd Argument)
//Restart the device - NOT StartUP !
//Argument 0 - IOS Device ID
-u {0} restart

Example:
 
LaunchCommandLineApp("idevicedebug", "-u 1234567890-1234567890 restart");

Get Device Information

Gets all the information about the connected device, for example, the version of OS on the device.
Executable Tool (1st Argument):

ideviceinfo

Arguments (2nd Argument)
//Get all the info about the device as XML
//Argument 0 - IOS Device ID
//Argument 1 - Information Key
-u {0} -k {1}

Keys (here are 4 examples - there are lots more !):
  • ProductVersion - OS Version
  • ActivationState - Activate State
  • CPUArchitecture - CPU Type
  • EthernetAddress - Mac Address
Example:
 
LaunchCommandLineApp("ideviceinfo", "-u 1234567890-1234567890 -k ProductVersion");

Pair the Device

This causes the trust dialog to pop up on the device, you must manually accept this in order to have comms between the Windows PC and the device.

Executable Tool (1st Argument):

idevicepair

Arguments (2nd Argument)
//Pair the device with this host
//Argument 0 - IOS Device ID
pair -u {0}

Example:
 
LaunchCommandLineApp("idevicepair", "-u 1234567890-1234567890 pair");
Note use the following:
  • pair to pair
  • unpair to un-pair
  • validate to validate

Is the Application Installed on the Device/List All Applications Installed on Device?

You can test to see if the application is installed on the device. This actually lists all the applications installed on the device, so get the return from the tool, and do a String.Contains call on it with the app name.

Executable Tool (1st Argument):
 ideviceinstaller

Arguments (2nd Argument)
//List all applications installed on the device
//Argument 0 = IOS Device to ls
-u {0} -l -o xml

Example:
 
LaunchCommandLineApp("ideviceinstaller", "-u 1234567890-1234567890 -l -o xml");

Install/Unistall an Application

This will install the specified application (.ipa file), on to the device. Note the .ipa will be verified to make sure it is a valid .ipa, and it has been signed, the identity matches and the provising DUID matches.

Executable Tool (1st Argument):
 ideviceinstaller

Arguments (2nd Argument)
//Installing an Application
//Argument 0 = Device Unique Identifier
//Argument 1 = Path to the Application name (.ipa file)
-u {0} -i {1}

Example:
 
LaunchCommandLineApp("ideviceinstaller", "-u 1234567890-1234567890 -i MyNumberOneAppInIstore.ipa"); 
 Note for un-installing use -u <DUID> -U <app name>, and note that the app name is the .app file, not the .ipa file name.

Conclusion

There are enough examples here showing how to talk to, and manipulate an iOS device from Windows. So that's it! Good luck with your iOS device programming from a Windows PC !

Many thanks to the guys at Quamotion - especially Frederik Carlier.