private async Task <CommandStatus> InstallAppAsync(
            AppInfo installedAppInfo,
            Version installedAppVersion,
            string connectionString,
            IDictionary <string, AppInfo> installedApps,
            AppxManagementDataContract.AppDesiredState desiredState)
        {
            Logger.Log("Processing install request for " + desiredState.packageFamilyId, LoggingLevel.Verbose);

            if (installedAppInfo == null)
            {
                // It is a new application.
                Logger.Log("    Can't find an installed version... Installing a fresh copy...", LoggingLevel.Verbose);
                await InstallAppFromAzureAsync(installedAppInfo, connectionString, desiredState, false /*not self update*/);       // ---> InstallAppFromAzureAsync
            }
            else
            {
                Windows.ApplicationModel.PackageId thisPackage = Windows.ApplicationModel.Package.Current.Id;
                Debug.WriteLine("FamilyName = " + thisPackage.FamilyName);
                Debug.WriteLine("Name       = " + thisPackage.Name);

                bool isSelf = desiredState.packageFamilyName == thisPackage.FamilyName;

                // A version of this application is installed.
                Logger.Log("    Found an installed version...", LoggingLevel.Verbose);

                if (desiredState.version == installedAppVersion)
                {
                    Logger.Log("        Same version is installed...", LoggingLevel.Verbose);
                    AppxManagementDataContract.AppReportedState appReportedState = null;

                    if (AppUtils.StartUpTypeToMessage(desiredState.startUp) == installedAppInfo.StartUp)
                    {
                        Logger.Log("        App StartUp is the same: desired = " + desiredState.startUp, LoggingLevel.Verbose);

                        appReportedState = new AppxManagementDataContract.AppReportedState(
                            desiredState.packageFamilyId,
                            desiredState.packageFamilyName,
                            installedAppVersion.ToString(),
                            AppUtils.StartUpTypeFromMessage(installedAppInfo.StartUp),
                            installedAppInfo.InstallDate,
                            0,
                            null,                 // no error
                            JsonReport.Report);
                    }
                    else
                    {
                        Logger.Log("        App StartUp is different: desired = " + desiredState.startUp.ToString() + ", current = " + installedAppInfo.StartUp.ToString(), LoggingLevel.Verbose);

                        switch (desiredState.startUp)
                        {
                        case AppxStartUpType.None:
                        {
                            bool background = installedAppInfo.StartUp == StartUpType.Background;
                            if (background)
                            {
                                Logger.Log("            Removing app from background apps.", LoggingLevel.Verbose);
                                StartupAppInfo startupAppInfo = new StartupAppInfo(desiredState.packageFamilyName, background);
                                await RemoveStartupAppAsync(startupAppInfo);
                            }
                            else
                            {
                                Logger.Log("            App is no longer marked to be start-up app. Change takes effect when another app is set to be the start-up app.", LoggingLevel.Verbose);
                            }
                        }
                        break;

                        case AppxStartUpType.Foreground:
                        {
                            Logger.Log("            Setting app to be the foreground app.", LoggingLevel.Verbose);
                            StartupAppInfo startupAppInfo = new StartupAppInfo(desiredState.packageFamilyName, false /*background*/);
                            await AddStartupAppAsync(startupAppInfo);
                        }
                        break;

                        case AppxStartUpType.Background:
                        {
                            Logger.Log("            Adding app to the background apps.", LoggingLevel.Verbose);
                            StartupAppInfo startupAppInfo = new StartupAppInfo(desiredState.packageFamilyName, true /*background*/);
                            await AddStartupAppAsync(startupAppInfo);
                        }
                        break;
                        }

                        AppxStartUpType appStartUp = await GetAppStartup(desiredState.packageFamilyName);

                        Logger.Log("            Querying returned app startup: " + appStartUp.ToString(), LoggingLevel.Verbose);

                        appReportedState = new AppxManagementDataContract.AppReportedState(
                            desiredState.packageFamilyId,
                            desiredState.packageFamilyName,
                            installedAppVersion.ToString(),
                            appStartUp,
                            installedAppInfo.InstallDate,
                            0,
                            null,                 // no error
                            JsonReport.Report);
                    }

                    _stateToReport[desiredState.packageFamilyId] = appReportedState;
                }
                else if (desiredState.version > installedAppVersion)
                {
                    Logger.Log("        Older version is installed...", LoggingLevel.Verbose);

                    if (!String.IsNullOrEmpty(desiredState.appxSource))
                    {
                        // Trigger the update...
                        await InstallAppFromAzureAsync(installedAppInfo, connectionString, desiredState, isSelf);

                        if (isSelf)
                        {
                            // If isSelf == true, it means that SystemConfigurator will force this application to exit very soon.
                            // Let's stop processing any further desired properties.
                            return(CommandStatus.PendingDMAppRestart);
                        }
                    }
                    else
                    {
                        // Note that store updates are not control through DM desired properties.
                        // Instead, they are triggered by the system scan for all store applications.
                        throw new Error(ErrorCodes.INVALID_DESIRED_APPX_SRC, "Appx package is required to update " + desiredState.packageFamilyName);
                    }
                }
                else
                {
                    // desiredState.version < installedAppVersion
                    Logger.Log("       Newer version is installed...rolling back.", LoggingLevel.Verbose);

                    if (String.IsNullOrEmpty(desiredState.appxSource))
                    {
                        AppxManagementDataContract.AppReportedState appReportedState = new AppxManagementDataContract.AppReportedState(
                            desiredState.packageFamilyId,
                            desiredState.packageFamilyName,
                            installedAppVersion.ToString(),
                            AppUtils.StartUpTypeFromMessage(installedAppInfo.StartUp),
                            installedAppInfo.InstallDate,
                            ErrorCodes.INVALID_DESIRED_APPX_SRC,
                            "Cannot install appx without a source.",
                            JsonReport.Report);

                        _stateToReport[desiredState.packageFamilyId] = appReportedState;

                        throw new Exception("Failed to roll back application version.");
                    }

                    if (isSelf)
                    {
                        // The reverting is implemented as an 'uninstall' followed by an 'install'.
                        // The package to be installed is downloaded in the 'install' step.
                        // So, if we were to revert self to an older version, we would have to:
                        //  - (1) download the old version first,
                        //  - (2) send an atomic uninstall-install request to SystemConfigurator to
                        //        uninstall and follow with an install and re-launch.
                        //  The above two steps are not implemented yet.
                        throw new Error(ErrorCodes.CANNOT_REVERT_DM_APPLICATION, "Reverting to an older version of the device management application (" + desiredState.packageFamilyName + ") is not supported.");
                    }
                    // Note that UninstallAppAsync will throw if it fails - and correctly avoid launching the install...
                    await UninstallAppAsync(installedAppInfo,
                                            packageFamilyId : desiredState.packageFamilyId,
                                            packageFamilyName : desiredState.packageFamilyName);
                    await InstallAppFromAzureAsync(installedAppInfo, connectionString, desiredState, isSelf);
                }
            }

            return(CommandStatus.Committed);
        }
        private async Task InstallAppFromAzureAsync(AppInfo currentState, string connectionString, AppxManagementDataContract.AppDesiredState desiredState, bool selfUpdate)
        {
            // Is this a fresh installation?
            if (currentState == null)
            {
                currentState = new AppInfo();
                currentState.PackageFamilyName = desiredState.packageFamilyName;
                currentState.Version           = AppxManagementDataContract.VersionNotInstalled;
                currentState.InstallDate       = "";
            }

            AppxManagementDataContract.AppReportedState reportedState = null;
            try
            {
                Logger.Log("-> installing " + desiredState.packageFamilyName + " from " + desiredState.appxSource, LoggingLevel.Verbose);
                if (String.IsNullOrEmpty(desiredState.appxSource))
                {
                    throw new Error(ErrorCodes.INVALID_DESIRED_APPX_SRC, "Cannot install appx without a source.");
                }

                var requestData = new AppInstallRequestData();
                requestData.PackageFamilyName = desiredState.packageFamilyName;
                requestData.StartUp           = AppUtils.StartUpTypeToMessage(desiredState.startUp);

                // Downloading dependencies...
                Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Downloading...", LoggingLevel.Verbose);
                if (!String.IsNullOrEmpty(desiredState.depsSources))
                {
                    string[] depsSources = desiredState.depsSources.Split(';');

                    for (int i = 0; i < depsSources.Length; ++i)
                    {
                        IoTDMClient.BlobInfo dependencyBlob = IoTDMClient.BlobInfo.BlobInfoFromSource(connectionString, depsSources[i]);
                        Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Downloading " + dependencyBlob.BlobName, LoggingLevel.Verbose);

                        var dependencyPath = await dependencyBlob.DownloadToTempAsync(this._systemConfiguratorProxy);

                        Logger.Log(dependencyPath, LoggingLevel.Verbose);
                        requestData.Dependencies.Add(dependencyPath);
                    }
                }

                // Downloading certificates...
                Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Downloading appx certificate...", LoggingLevel.Verbose);
                IoTDMClient.BlobInfo certificateBlob = IoTDMClient.BlobInfo.BlobInfoFromSource(connectionString, desiredState.certSource);
                requestData.CertFile = await certificateBlob.DownloadToTempAsync(this._systemConfiguratorProxy);

                requestData.CertStore = desiredState.certStore;

                // Downloading appx...
                Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Downloading appx...", LoggingLevel.Verbose);
                IoTDMClient.BlobInfo appxBlob = IoTDMClient.BlobInfo.BlobInfoFromSource(connectionString, desiredState.appxSource);
                requestData.AppxPath = await appxBlob.DownloadToTempAsync(this._systemConfiguratorProxy);

                requestData.IsDMSelfUpdate = selfUpdate;

                // Installing appx...
                Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Installing appx...", LoggingLevel.Verbose);
                AppInstallResponse response = await InstallAppAsync(requestData);

                if (response.data.pending)
                {
                    Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Installing appx is pending...", LoggingLevel.Verbose);
                    Debug.Assert(selfUpdate);

                    reportedState = new AppxManagementDataContract.AppReportedState(desiredState.packageFamilyId,
                                                                                    desiredState.packageFamilyName,
                                                                                    CommonDataContract.JsonPending,
                                                                                    AppUtils.StartUpTypeFromMessage(response.data.startUp),
                                                                                    response.data.installDate,
                                                                                    0,
                                                                                    null,
                                                                                    JsonReport.Report);
                    _stateToReport[desiredState.packageFamilyId] = reportedState;
                    return;
                }
                else
                {
                    Logger.Log(DateTime.Now.ToString("HH:mm:ss") + " Done installing appx...", LoggingLevel.Verbose);

                    Error e = null;
                    if (desiredState.version.ToString() != response.data.version)
                    {
                        if (currentState.Version == response.data.version)
                        {
                            e = new Error(ErrorCodes.INVALID_INSTALLED_APP_VERSION_UNCHANGED, "Installating the supplied appx succeeded, but the new app version is not the desired version, and is the same as the old app version.");
                        }
                        else
                        {
                            e = new Error(ErrorCodes.INVALID_INSTALLED_APP_VERSION_UNEXPECTED, "Installating the supplied appx succeeded, but the new app version is not the desired version.");
                        }
                    }

                    reportedState = new AppxManagementDataContract.AppReportedState(desiredState.packageFamilyId,
                                                                                    desiredState.packageFamilyName,
                                                                                    response.data.version,
                                                                                    AppUtils.StartUpTypeFromMessage(response.data.startUp),
                                                                                    response.data.installDate,
                                                                                    (e != null? e.HResult : 0),
                                                                                    (e != null ? e.Message : null),
                                                                                    JsonReport.Report);
                    _stateToReport[desiredState.packageFamilyId] = reportedState;
                    return;
                }
            }
            catch (Error e)
            {
                reportedState = new AppxManagementDataContract.AppReportedState(desiredState.packageFamilyId,
                                                                                desiredState.packageFamilyName,
                                                                                currentState.Version,
                                                                                AppUtils.StartUpTypeFromMessage(currentState.StartUp),
                                                                                currentState.InstallDate, // install date
                                                                                e.HResult,
                                                                                e.Message,
                                                                                JsonReport.Report);
            }
            catch (Exception e)
            {
                reportedState = new AppxManagementDataContract.AppReportedState(desiredState.packageFamilyId,
                                                                                desiredState.packageFamilyName,
                                                                                currentState.Version,
                                                                                AppUtils.StartUpTypeFromMessage(currentState.StartUp),
                                                                                currentState.InstallDate, // install date
                                                                                e.HResult,
                                                                                e.Message,
                                                                                JsonReport.Report);
            }
            _stateToReport[desiredState.packageFamilyId] = reportedState;
            throw new Exception("Failed to install " + desiredState.packageFamilyName);
        }