Esempio n. 1
0
        public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
        {
            bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0;

            CommonUtil.logd("onFillRequest " + (isManual ? "manual" : "auto"));
            var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;

            //TODO support package signature verification as soon as this is supported in Keepass storage

            var clientState = request.ClientState;

            CommonUtil.logd("onFillRequest(): data=" + CommonUtil.BundleToString(clientState));


            cancellationSignal.CancelEvent += (sender, e) => {
                Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet.");
            };
            // Parse AutoFill data in Activity
            string query  = null;
            var    parser = new StructureParser(this, structure);

            try
            {
                query = parser.ParseForFill(isManual);
            }
            catch (Java.Lang.SecurityException e)
            {
                Log.Warn(CommonUtil.Tag, "Security exception handling request");
                callback.OnFailure(e.Message);
                return;
            }

            AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;


            var autofillIds = autofillFields.GetAutofillIds();

            if (autofillIds.Length != 0 && CanAutofill(query, isManual))
            {
                var responseBuilder = new FillResponse.Builder();

                var  entryDataset    = AddEntryDataset(query, parser);
                bool hasEntryDataset = entryDataset != null;
                if (entryDataset != null)
                {
                    responseBuilder.AddDataset(entryDataset);
                }

                AddQueryDataset(query, isManual, autofillIds, responseBuilder, !hasEntryDataset);
                AddDisableDataset(query, autofillIds, responseBuilder, isManual);
                responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
                                                                 parser.AutofillFields.GetAutofillIds()).Build());

                callback.OnSuccess(responseBuilder.Build());
            }
            else
            {
                callback.OnSuccess(null);
            }
        }
Esempio n. 2
0
        public static string[] FilterForSupportedHints(string[] hints)
        {
            var filteredHints = new string[hints.Length];
            int i             = 0;

            foreach (var hint in hints)
            {
                if (IsSupportedHint(hint))
                {
                    filteredHints[i++] = hint;
                }
                else
                {
                    CommonUtil.logd("Invalid autofill hint: " + hint);
                }
            }
            var finalFilteredHints = new string[i];

            Array.Copy(filteredHints, 0, finalFilteredHints, 0, i);
            return(finalFilteredHints);
        }
Esempio n. 3
0
        public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
        {
            bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0;

            CommonUtil.logd("onFillRequest " + (isManual ? "manual" : "auto"));
            var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;

            //TODO support package signature verification as soon as this is supported in Keepass storage

            var clientState = request.ClientState;

            CommonUtil.logd("onFillRequest(): data=" + CommonUtil.BundleToString(clientState));


            cancellationSignal.CancelEvent += (sender, e) => {
                Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet.");
            };
            // Parse AutoFill data in Activity
            StructureParser.AutofillTargetId query = null;
            var parser = new StructureParser(this, structure);

            try
            {
                query = parser.ParseForFill(isManual);
            }
            catch (Java.Lang.SecurityException e)
            {
                Log.Warn(CommonUtil.Tag, "Security exception handling request");
                callback.OnFailure(e.Message);
                return;
            }

            AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;


            var autofillIds = autofillFields.GetAutofillIds();

            if (autofillIds.Length != 0 && CanAutofill(query, isManual))
            {
                var responseBuilder = new FillResponse.Builder();

                Dataset entryDataset = null;
                if (query.IncompatiblePackageAndDomain == false)
                {
                    //domain and package are compatible. Use Domain if available and package otherwise. Can fill without warning.
                    entryDataset = BuildEntryDataset(query.DomainOrPackage, query.WebDomain, query.PackageName, autofillIds, parser, DisplayWarning.None);
                }
                else
                {
                    //domain or package are incompatible. Don't show the entry. (Tried to do so first but behavior was not consistent)
                    //entryDataset = BuildEntryDataset(query.WebDomain, query.WebDomain, query.PackageName, autofillIds, parser, DisplayWarning.FillDomainInUntrustedApp);
                }
                bool hasEntryDataset = entryDataset != null;
                if (entryDataset != null)
                {
                    responseBuilder.AddDataset(entryDataset);
                }

                if (query.WebDomain != null)
                {
                    AddQueryDataset(query.WebDomain,
                                    query.WebDomain, query.PackageName,
                                    isManual, autofillIds, responseBuilder, !hasEntryDataset, query.IncompatiblePackageAndDomain ? DisplayWarning.FillDomainInUntrustedApp : DisplayWarning.None);
                }
                else
                {
                    AddQueryDataset(query.PackageNameWithPseudoSchema,
                                    query.WebDomain, query.PackageName,
                                    isManual, autofillIds, responseBuilder, !hasEntryDataset, DisplayWarning.None);
                }


                AddDisableDataset(query.DomainOrPackage, autofillIds, responseBuilder, isManual);

                if (PreferenceManager.GetDefaultSharedPreferences(this)
                    .GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
                {
                    if (!CompatBrowsers.Contains(parser.PackageId))
                    {
                        responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
                                                                         parser.AutofillFields.GetAutofillIds()).Build());
                    }
                }

                callback.OnSuccess(responseBuilder.Build());
            }
            else
            {
                callback.OnSuccess(null);
            }
        }
Esempio n. 4
0
 public override void OnDisconnected()
 {
     CommonUtil.logd("onDisconnected");
 }
        void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain)
        {
            String webDomain = viewNode.WebDomain;

            if (webDomain != null)
            {
                Log.Debug(CommonUtil.Tag, $"child web domain: {webDomain}");
                if (!string.IsNullOrEmpty(validWebdomain))
                {
                    if (webDomain == validWebdomain)
                    {
                        throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {validWebdomain}, child={webDomain}");
                    }
                }
                else
                {
                    validWebdomain = webDomain;
                }
            }

            string[] viewHints = viewNode.GetAutofillHints();
            if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused &&
                isManualRequest)
            {
                viewHints[0] = "on";
            }
            CommonUtil.logd("viewHints=" + viewHints);
            CommonUtil.logd("class=" + viewNode.ClassName);
            CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
            if (viewNode?.HtmlInfo?.Tag == "input")
            {
                foreach (var p in viewNode.HtmlInfo.Attributes)
                {
                    CommonUtil.logd("attr=" + p.First + "/" + p.Second);
                }
            }
            if (viewHints != null && viewHints.Length > 0 && viewHints.First() != "on" /*if hint is "on", treat as if there is no hint*/)
            {
                if (forFill)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(viewNode));
                }
                else
                {
                    //TODO implement save
                    throw new NotImplementedException("TODO: Port and use AutoFill hints");
                    //ClientFormData.Add(new FilledAutofillField(viewNode));
                }
            }
            else
            {
                if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input")
                {
                    _editTextsWithoutHint.Add(viewNode);
                }
            }
            var childrenSize = viewNode.ChildCount;

            if (childrenSize > 0)
            {
                for (int i = 0; i < childrenSize; i++)
                {
                    ParseLocked(forFill, isManualRequest, viewNode.GetChildAt(i), ref validWebdomain);
                }
            }
        }
Esempio n. 6
0
 public override void OnDisconnected()
 {
     _lockTime = DateTime.MinValue;
     CommonUtil.logd("onDisconnected");
 }
Esempio n. 7
0
        public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
        {
            bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0;

            CommonUtil.logd("onFillRequest " + (isManual ? "manual" : "auto"));
            var structure = request.FillContexts.Last().Structure;


            if (_lockTime + _lockTimeout < DateTime.Now)
            {
                _lockTime = DateTime.Now;

                //TODO support package signature verification as soon as this is supported in Keepass storage

                var clientState = request.ClientState;
                CommonUtil.logd("onFillRequest(): data=" + CommonUtil.BundleToString(clientState));


                cancellationSignal.CancelEvent += (sender, e) =>
                {
                    Kp2aLog.Log("Cancel autofill not implemented yet.");
                    _lockTime = DateTime.MinValue;
                };
                // Parse AutoFill data in Activity
                StructureParser.AutofillTargetId query = null;
                var parser = new StructureParser(this, structure);
                try
                {
                    query = parser.ParseForFill(isManual);
                }
                catch (Java.Lang.SecurityException e)
                {
                    Log.Warn(CommonUtil.Tag, "Security exception handling request");
                    callback.OnFailure(e.Message);
                    return;
                }

                AutofillFieldMetadataCollection autofillFields           = parser.AutofillFields;
                InlineSuggestionsRequest        inlineSuggestionsRequest = null;
                IList <InlinePresentationSpec>  inlinePresentationSpecs  = null;
                if (((int)Build.VERSION.SdkInt >= 30) &&
                    (PreferenceManager.GetDefaultSharedPreferences(this).GetBoolean(GetString(Resource.String.InlineSuggestions_key), true)))
                {
                    inlineSuggestionsRequest = request.InlineSuggestionsRequest;

                    inlinePresentationSpecs = inlineSuggestionsRequest?.InlinePresentationSpecs;
                }


                var autofillIds = autofillFields.GetAutofillIds();
                if (autofillIds.Length != 0 && CanAutofill(query, isManual))
                {
                    var responseBuilder = new FillResponse.Builder();

                    bool hasEntryDataset = false;

                    IList <Dataset> entryDatasets = new List <Dataset>();
                    if (query.IncompatiblePackageAndDomain == false)
                    {
                        Kp2aLog.Log("AF: (query.IncompatiblePackageAndDomain == false)");
                        //domain and package are compatible. Use Domain if available and package otherwise. Can fill without warning.
                        entryDatasets = BuildEntryDatasets(query.DomainOrPackage, query.WebDomain,
                                                           query.PackageName,
                                                           autofillIds, parser, DisplayWarning.None,
                                                           inlinePresentationSpecs
                                                           ).Where(ds => ds != null).ToList();
                        if (entryDatasets.Count > inlineSuggestionsRequest?.MaxSuggestionCount - 2 /*disable dataset and query*/)
                        {
                            //we have too many elements. disable inline suggestions
                            inlinePresentationSpecs = null;
                            entryDatasets           = BuildEntryDatasets(query.DomainOrPackage, query.WebDomain,
                                                                         query.PackageName,
                                                                         autofillIds, parser, DisplayWarning.None,
                                                                         null
                                                                         ).Where(ds => ds != null).ToList();
                        }
                        foreach (var entryDataset in entryDatasets
                                 )
                        {
                            Kp2aLog.Log("AF: Got EntryDataset " + (entryDataset == null));
                            responseBuilder.AddDataset(entryDataset);
                            hasEntryDataset = true;
                        }
                    }



                    {
                        if (query.WebDomain != null)
                        {
                            AddQueryDataset(query.WebDomain,
                                            query.WebDomain, query.PackageName,
                                            isManual, autofillIds, responseBuilder, !hasEntryDataset,
                                            query.IncompatiblePackageAndDomain
                                    ? DisplayWarning.FillDomainInUntrustedApp
                                    : DisplayWarning.None,
                                            AutofillHelper.ExtractSpec(inlinePresentationSpecs, entryDatasets.Count));
                        }
                        else
                        {
                            AddQueryDataset(query.PackageNameWithPseudoSchema,
                                            query.WebDomain, query.PackageName,
                                            isManual, autofillIds, responseBuilder, !hasEntryDataset, DisplayWarning.None,
                                            AutofillHelper.ExtractSpec(inlinePresentationSpecs, entryDatasets.Count));
                        }
                    }

                    if (!PreferenceManager.GetDefaultSharedPreferences(this)
                        .GetBoolean(GetString(Resource.String.NoAutofillDisabling_key), false))
                    {
                        AddDisableDataset(query.DomainOrPackage, autofillIds, responseBuilder, isManual, AutofillHelper.ExtractSpec(inlinePresentationSpecs, entryDatasets.Count));
                    }

                    if (PreferenceManager.GetDefaultSharedPreferences(this)
                        .GetBoolean(GetString(Resource.String.OfferSaveCredentials_key), true))
                    {
                        if (!CompatBrowsers.Contains(parser.PackageId))
                        {
                            responseBuilder.SetSaveInfo(new SaveInfo.Builder(parser.AutofillFields.SaveType,
                                                                             parser.AutofillFields.GetAutofillIds()).Build());
                        }
                    }

                    Kp2aLog.Log("return autofill success");
                    callback.OnSuccess(responseBuilder.Build());
                }
                else
                {
                    Kp2aLog.Log("cannot autofill");
                    callback.OnSuccess(null);
                }
            }
            else
            {
                Kp2aLog.Log("Ignoring onFillRequest as there is another request going on.");
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Traverse AssistStructure and add ViewNode metadata to a flat list.
        /// </summary>
        /// <returns>The parse.</returns>
        /// <param name="forFill">If set to <c>true</c> for fill.</param>
        /// <param name="isManualRequest"></param>
        string Parse(bool forFill, bool isManualRequest)
        {
            CommonUtil.logd("Parsing structure for " + Structure.ActivityComponent);
            var nodes = Structure.WindowNodeCount;

            ClientFormData = new FilledAutofillFieldCollection();
            String webDomain = null;

            _editTextsWithoutHint.Clear();

            for (int i = 0; i < nodes; i++)
            {
                var node = Structure.GetWindowNodeAt(i);
                var view = node.RootViewNode;
                ParseLocked(forFill, isManualRequest, view, ref webDomain);
            }



            List <AssistStructure.ViewNode> passwordFields = new List <AssistStructure.ViewNode>();
            List <AssistStructure.ViewNode> usernameFields = new List <AssistStructure.ViewNode>();

            if (AutofillFields.Empty)
            {
                passwordFields = _editTextsWithoutHint.Where(IsPassword).ToList();
                if (!passwordFields.Any())
                {
                    passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList();
                }

                foreach (var passwordField in passwordFields)
                {
                    var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
                    if (usernameField != null)
                    {
                        usernameFields.Add(usernameField);
                    }
                }
                //for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
                //let's assume it is a username field:
                if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
                {
                    usernameFields.Add(_editTextsWithoutHint.First());
                }
            }

            //force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints)
            if (isManualRequest)
            {
                foreach (AssistStructure.ViewNode editText in _editTextsWithoutHint)
                {
                    if (editText.IsFocused)
                    {
                        if (IsPassword(editText) || HasPasswordHint(editText))
                        {
                            passwordFields.Add(editText);
                        }
                        else
                        {
                            usernameFields.Add(editText);
                        }
                        break;
                    }
                }
            }

            if (forFill)
            {
                foreach (var uf in usernameFields)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(uf, new[] { View.AutofillHintUsername }));
                }
                foreach (var pf in passwordFields)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(pf, new[] { View.AutofillHintPassword }));
                }
            }
            else
            {
                foreach (var uf in usernameFields)
                {
                    ClientFormData.Add(new FilledAutofillField(uf, new[] { View.AutofillHintUsername }));
                }
                foreach (var pf in passwordFields)
                {
                    ClientFormData.Add(new FilledAutofillField(pf, new[] { View.AutofillHintPassword }));
                }
            }



            String packageName = Structure.ActivityComponent.PackageName;

            if (!string.IsNullOrEmpty(webDomain))
            {
                bool valid = Kp2aDigitalAssetLinksDataSource.Instance.IsValid(mContext, webDomain, packageName);
                if (!valid)
                {
                    CommonUtil.loge($"DAL verification failed for {packageName}/{webDomain}");
                    webDomain = null;
                }
            }
            if (string.IsNullOrEmpty(webDomain))
            {
                webDomain = "androidapp://" + packageName;
                CommonUtil.logd("no web domain. Using package name.");
            }
            return(webDomain);
        }
Esempio n. 9
0
        void ParseLocked(bool forFill, bool isManualRequest, AssistStructure.ViewNode viewNode, ref string validWebdomain)
        {
            String webDomain = viewNode.WebDomain;

            if ((PackageId == null) && (!string.IsNullOrWhiteSpace(viewNode.IdPackage)) &&
                (viewNode.IdPackage != "android"))
            {
                PackageId = viewNode.IdPackage;
            }

            DomainName outDomain;

            if (DomainName.TryParse(webDomain, domainSuffixParserCache, out outDomain))
            {
                webDomain = outDomain.RegisterableDomainName;
            }

            if (webDomain != null)
            {
                if (!string.IsNullOrEmpty(validWebdomain))
                {
                    if (webDomain != validWebdomain)
                    {
                        throw new Java.Lang.SecurityException($"Found multiple web domains: valid= {validWebdomain}, child={webDomain}");
                    }
                }
                else
                {
                    validWebdomain = webDomain;
                }
            }

            string[] viewHints = viewNode.GetAutofillHints();
            if (viewHints != null && viewHints.Length == 1 && viewHints.First() == "off" && viewNode.IsFocused &&
                isManualRequest)
            {
                viewHints[0] = "on";
            }
            if (viewHints != null && viewHints.Any())
            {
                CommonUtil.logd("viewHints=" + viewHints);
                CommonUtil.logd("class=" + viewNode.ClassName);
                CommonUtil.logd("tag=" + (viewNode?.HtmlInfo?.Tag ?? "(null)"));
            }

            if (viewNode?.HtmlInfo?.Tag == "input")
            {
                foreach (var p in viewNode.HtmlInfo.Attributes)
                {
                    CommonUtil.logd("attr=" + p.First + "/" + p.Second);
                }
            }
            if (viewHints != null && viewHints.Length > 0 && viewHints.First() != "on" /*if hint is "on", treat as if there is no hint*/)
            {
                if (forFill)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(viewNode));
                }
                else
                {
                    FilledAutofillField filledAutofillField = new FilledAutofillField(viewNode);
                    ClientFormData.Add(filledAutofillField);
                }
            }
            else
            {
                if (viewNode.ClassName == "android.widget.EditText" || viewNode?.HtmlInfo?.Tag == "input")
                {
                    _editTextsWithoutHint.Add(viewNode);
                }
            }
            var childrenSize = viewNode.ChildCount;

            if (childrenSize > 0)
            {
                for (int i = 0; i < childrenSize; i++)
                {
                    ParseLocked(forFill, isManualRequest, viewNode.GetChildAt(i), ref validWebdomain);
                }
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Traverse AssistStructure and add ViewNode metadata to a flat list.
        /// </summary>
        /// <returns>The parse.</returns>
        /// <param name="forFill">If set to <c>true</c> for fill.</param>
        /// <param name="isManualRequest"></param>
        AutofillTargetId Parse(bool forFill, bool isManualRequest)
        {
            AutofillTargetId result = new AutofillTargetId();

            CommonUtil.logd("Parsing structure for " + Structure.ActivityComponent);
            var nodes = Structure.WindowNodeCount;

            ClientFormData = new FilledAutofillFieldCollection();
            String webDomain = null;

            _editTextsWithoutHint.Clear();

            for (int i = 0; i < nodes; i++)
            {
                var node = Structure.GetWindowNodeAt(i);

                var view = node.RootViewNode;
                ParseLocked(forFill, isManualRequest, view, ref webDomain);
            }



            List <AssistStructure.ViewNode> passwordFields = new List <AssistStructure.ViewNode>();
            List <AssistStructure.ViewNode> usernameFields = new List <AssistStructure.ViewNode>();

            if (AutofillFields.Empty)
            {
                passwordFields = _editTextsWithoutHint.Where(IsPassword).ToList();
                if (!passwordFields.Any())
                {
                    passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList();
                }

                usernameFields = _editTextsWithoutHint.Where(HasUsernameHint).ToList();

                if (usernameFields.Any() == false)
                {
                    foreach (var passwordField in passwordFields)
                    {
                        var usernameField = _editTextsWithoutHint
                                            .TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault();
                        if (usernameField != null)
                        {
                            usernameFields.Add(usernameField);
                        }
                    }
                }
                if (usernameFields.Any() == false)
                {
                    //for some pages with two-step login, we don't see a password field and don't display the autofill for non-manual requests. But if the user forces autofill,
                    //let's assume it is a username field:
                    if (isManualRequest && !passwordFields.Any() && _editTextsWithoutHint.Count == 1)
                    {
                        usernameFields.Add(_editTextsWithoutHint.First());
                    }
                }
            }

            //force focused fields to be included in autofill fields when request was triggered manually. This allows to fill fields which are "off" or don't have a hint (in case there are hints)
            if (isManualRequest)
            {
                foreach (AssistStructure.ViewNode editText in _editTextsWithoutHint)
                {
                    if (editText.IsFocused)
                    {
                        if (IsPassword(editText) || HasPasswordHint(editText))
                        {
                            passwordFields.Add(editText);
                        }
                        else
                        {
                            usernameFields.Add(editText);
                        }
                        break;
                    }
                }
            }

            if (forFill)
            {
                foreach (var uf in usernameFields)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(uf, new[] { View.AutofillHintUsername }));
                }
                foreach (var pf in passwordFields)
                {
                    AutofillFields.Add(new AutofillFieldMetadata(pf, new[] { View.AutofillHintPassword }));
                }
            }
            else
            {
                foreach (var uf in usernameFields)
                {
                    ClientFormData.Add(new FilledAutofillField(uf, new[] { View.AutofillHintUsername }));
                }
                foreach (var pf in passwordFields)
                {
                    ClientFormData.Add(new FilledAutofillField(pf, new[] { View.AutofillHintPassword }));
                }
            }


            result.WebDomain   = webDomain;
            result.PackageName = Structure.ActivityComponent.PackageName;
            if (!string.IsNullOrEmpty(webDomain) && !PreferenceManager.GetDefaultSharedPreferences(mContext).GetBoolean(mContext.GetString(Resource.String.NoDalVerification_key), false))
            {
                result.IncompatiblePackageAndDomain = !kp2aDigitalAssetLinksDataSource.IsTrustedLink(webDomain, result.PackageName);
                if (result.IncompatiblePackageAndDomain)
                {
                    CommonUtil.loge($"DAL verification failed for {result.PackageName}/{result.WebDomain}");
                }
            }
            else
            {
                result.IncompatiblePackageAndDomain = false;
            }
            return(result);
        }
Esempio n. 11
0
 public override void OnDisconnected()
 {
     _lock.Set(false);
     CommonUtil.logd("onDisconnected");
 }
Esempio n. 12
0
        public override void OnFillRequest(FillRequest request, CancellationSignal cancellationSignal, FillCallback callback)
        {
            bool isManual = (request.Flags & FillRequest.FlagManualRequest) != 0;

            CommonUtil.logd("onFillRequest " + (isManual ? "manual" : "auto"));
            var structure = request.FillContexts[request.FillContexts.Count - 1].Structure;

            //TODO support package signature verification as soon as this is supported in Keepass storage

            var clientState = request.ClientState;

            CommonUtil.logd("onFillRequest(): data=" + CommonUtil.BundleToString(clientState));


            cancellationSignal.CancelEvent += (sender, e) => {
                Log.Warn(CommonUtil.Tag, "Cancel autofill not implemented yet.");
            };
            // Parse AutoFill data in Activity
            string query  = null;
            var    parser = new StructureParser(this, structure);

            try
            {
                query = parser.ParseForFill(isManual);
            }
            catch (Java.Lang.SecurityException e)
            {
                Log.Warn(CommonUtil.Tag, "Security exception handling request");
                callback.OnFailure(e.Message);
                return;
            }

            AutofillFieldMetadataCollection autofillFields = parser.AutofillFields;

            bool responseAuth = true;
            var  autofillIds  = autofillFields.GetAutofillIds();

            if (responseAuth && autofillIds.Length != 0 && CanAutofill(query))
            {
                var responseBuilder = new FillResponse.Builder();

                var         sender       = IntentBuilder.GetAuthIntentSenderForResponse(this, query, isManual);
                RemoteViews presentation = AutofillHelper.NewRemoteViews(PackageName, GetString(Resource.String.autofill_sign_in_prompt), AppNames.LauncherIcon);

                var datasetBuilder = new Dataset.Builder(presentation);
                datasetBuilder.SetAuthentication(sender);
                //need to add placeholders so we can directly fill after ChooseActivity
                foreach (var autofillId in autofillIds)
                {
                    datasetBuilder.SetValue(autofillId, AutofillValue.ForText("PLACEHOLDER"));
                }

                responseBuilder.AddDataset(datasetBuilder.Build());

                callback.OnSuccess(responseBuilder.Build());
            }
            else
            {
                var datasetAuth = true;
                var response    = AutofillHelper.NewResponse(this, datasetAuth, autofillFields, null, IntentBuilder);
                callback.OnSuccess(response);
            }
        }