void ParseLocked(bool forFill, ViewNode viewNode) { if (viewNode.GetAutofillHints() != null && viewNode.GetAutofillHints().Length > 0) { if (forFill) { AutofillFields.Add(new AutofillFieldMetadata(viewNode)); } else { ClientFormData.Add(new FilledAutofillField(viewNode)); } } var childrenSize = viewNode.ChildCount; if (childrenSize > 0) { for (int i = 0; i < childrenSize; i++) { ParseLocked(forFill, viewNode.GetChildAt(i)); } } }
/// <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) { Log.Debug(CommonUtil.Tag, "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); } if (AutofillFields.Empty) { var passwordFields = _editTextsWithoutHint .Where(IsPassword).ToList(); if (!passwordFields.Any()) { passwordFields = _editTextsWithoutHint.Where(HasPasswordHint).ToList(); } foreach (var passwordField in passwordFields) { AutofillFields.Add(new AutofillFieldMetadata(passwordField, new[] { View.AutofillHintPassword })); var usernameField = _editTextsWithoutHint.TakeWhile(f => f.AutofillId != passwordField.AutofillId).LastOrDefault(); if (usernameField != null) { AutofillFields.Add(new AutofillFieldMetadata(usernameField, new[] { View.AutofillHintUsername })); } } //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) { AutofillFields.Add(new AutofillFieldMetadata(_editTextsWithoutHint.First(), new[] { View.AutofillHintUsername })); } } //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) { AutofillFields.Add(new AutofillFieldMetadata(editText, new[] { IsPassword(editText) || HasPasswordHint(editText) ? View.AutofillHintPassword : View.AutofillHintUsername })); break; } } } 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; Log.Debug(CommonUtil.Tag, "no web domain. Using package name."); } return(webDomain); }
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); } } }
/// <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)) { 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); }
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); } } }