Loading core/java/android/service/autofill/AutofillService.java +71 −1 Original line number Original line Diff line number Diff line Loading @@ -280,6 +280,76 @@ import com.android.internal.os.SomeArgs; * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are * focused. * focused. * * <h3>Web security</h3> * * <p>When handling autofill requests that represent web pages (typically * view structures whose root's {@link android.app.assist.AssistStructure.ViewNode#getClassName()} * is a {@link android.webkit.WebView}), the service should take the following steps to verify if * the structure can be autofilled with the data associated with the app requesting it: * * <ol> * <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the * source of the document. * <li>Get the canonical domain using the * <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below). * <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a> * to obtain the package name and certificate fingerprint of the package corresponding to * the canonical domain. * <li>Make sure the certificate fingerprint matches the value returned by Package Manager * (see "Package verification" section above). * </ol> * * <p>Here's an example on how to get the canonical domain using * <a href="https://github.com/google/guava">Guava</a>: * * <pre class="prettyprint"> * private static String getCanonicalDomain(String domain) { * InternetDomainName idn = InternetDomainName.from(domain); * while (!idn.isTopPrivateDomain() && idn != null) { * idn = idn.parent(); * } * return idn == null ? null : idn.toString(); * } * </pre> * * <p>If the association between the web domain and app package cannot be verified through the steps * above, the service can still autofill the app, but it should warn the user about the potential * data leakage first, and askfor the user to confirm. For example, the service could: * * <ol> * <li>Create a dataset that requires * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authentication} to * unlock. * <li>Include the web domain in the custom presentation for the * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, android.widget.RemoteViews) * dataset value}. * <li>When the user select that dataset, show a disclaimer dialog explaining that the app is * requesting credentials for a web domain, but the service could not verify if the app owns * that domain. If the user agrees, then the service can unlock the dataset. * <li>Similarly, when adding a {@link SaveInfo} object for the request, the service should * include the above disclaimer in the {@link SaveInfo.Builder#setDescription(CharSequence)}. * </ol> * * <p>This same procedure could also be used when the autofillable data is contained inside an * {@code IFRAME}, in which case the WebView generates a new autofill context when a node inside * the {@code IFRAME} is focused, which the root node containing the {@code IFRAME}'s {@code src} * attribute on {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()}. A typical and * legitimate use case for this scenario is a financial app that allows the user * to login on different bank accounts. For example, a financial app {@code my_financial_app} could * use a WebView that loads contents from {@code banklogin.my_financial_app.com}, which contains an * {@code IFRAME} node whose {@code src} attribute is {@code login.some_bank.com}. When fulfilling * that request, the service could add an * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authenticated dataset} * whose presentation displays "Username for some_bank.com" and * "Password for some_bank.com". Then when the user taps one of these options, the service * shows the disclaimer dialog explaining that selecting that option would release the * {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees, * then the service returns an unlocked dataset with the {@code some_bank.com} credentials. * * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the * verifications above, as long as the service can verify the authenticity of the browser app by * checking its signing certificate. */ */ public abstract class AutofillService extends Service { public abstract class AutofillService extends Service { private static final String TAG = "AutofillService"; private static final String TAG = "AutofillService"; Loading Loading @@ -424,7 +494,7 @@ public abstract class AutofillService extends Service { * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) * to notify the result of the request. * to notify the result of the request. * * * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call * <p><b>Note:</b> To retrieve the actual value of the field, the service should call * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no * guarantee such method will return the most recent value of the field. * guarantee such method will return the most recent value of the field. Loading core/java/android/webkit/WebView.java +12 −0 Original line number Original line Diff line number Diff line Loading @@ -2725,6 +2725,18 @@ public class WebView extends AbsoluteLayout * understood by the {@link android.service.autofill.AutofillService} implementations: * understood by the {@link android.service.autofill.AutofillService} implementations: * * * <ol> * <ol> * <li>Only the HTML nodes inside a {@code FORM} are generated. * <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the * node representing the WebView. * <li>If a web page has multiple {@code FORM}s, only the data for the current form is * represented—if the user taps a field from another form, then the current autofill * context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and * a new context is created for that {@code FORM}. * <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in * the view structure until the user taps a field from a {@code FORM} inside the * {@code IFRAME}, in which case it would be treated the same way as multiple forms described * above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the * {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node. * <li>If the Android SDK provides a similar View, then should be set with the * <li>If the Android SDK provides a similar View, then should be set with the * fully-qualified class name of such view. * fully-qualified class name of such view. * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to Loading Loading
core/java/android/service/autofill/AutofillService.java +71 −1 Original line number Original line Diff line number Diff line Loading @@ -280,6 +280,76 @@ import com.android.internal.os.SomeArgs; * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger * calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are * a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are * focused. * focused. * * <h3>Web security</h3> * * <p>When handling autofill requests that represent web pages (typically * view structures whose root's {@link android.app.assist.AssistStructure.ViewNode#getClassName()} * is a {@link android.webkit.WebView}), the service should take the following steps to verify if * the structure can be autofilled with the data associated with the app requesting it: * * <ol> * <li>Use the {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()} to get the * source of the document. * <li>Get the canonical domain using the * <a href="https://publicsuffix.org/>Public Suffix List</a> (see example below). * <li>Use <a href="https://developers.google.com/digital-asset-links/">Digital Asset Links</a> * to obtain the package name and certificate fingerprint of the package corresponding to * the canonical domain. * <li>Make sure the certificate fingerprint matches the value returned by Package Manager * (see "Package verification" section above). * </ol> * * <p>Here's an example on how to get the canonical domain using * <a href="https://github.com/google/guava">Guava</a>: * * <pre class="prettyprint"> * private static String getCanonicalDomain(String domain) { * InternetDomainName idn = InternetDomainName.from(domain); * while (!idn.isTopPrivateDomain() && idn != null) { * idn = idn.parent(); * } * return idn == null ? null : idn.toString(); * } * </pre> * * <p>If the association between the web domain and app package cannot be verified through the steps * above, the service can still autofill the app, but it should warn the user about the potential * data leakage first, and askfor the user to confirm. For example, the service could: * * <ol> * <li>Create a dataset that requires * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authentication} to * unlock. * <li>Include the web domain in the custom presentation for the * {@link Dataset.Builder#setValue(AutofillId, AutofillValue, android.widget.RemoteViews) * dataset value}. * <li>When the user select that dataset, show a disclaimer dialog explaining that the app is * requesting credentials for a web domain, but the service could not verify if the app owns * that domain. If the user agrees, then the service can unlock the dataset. * <li>Similarly, when adding a {@link SaveInfo} object for the request, the service should * include the above disclaimer in the {@link SaveInfo.Builder#setDescription(CharSequence)}. * </ol> * * <p>This same procedure could also be used when the autofillable data is contained inside an * {@code IFRAME}, in which case the WebView generates a new autofill context when a node inside * the {@code IFRAME} is focused, which the root node containing the {@code IFRAME}'s {@code src} * attribute on {@link android.app.assist.AssistStructure.ViewNode#getWebDomain()}. A typical and * legitimate use case for this scenario is a financial app that allows the user * to login on different bank accounts. For example, a financial app {@code my_financial_app} could * use a WebView that loads contents from {@code banklogin.my_financial_app.com}, which contains an * {@code IFRAME} node whose {@code src} attribute is {@code login.some_bank.com}. When fulfilling * that request, the service could add an * {@link Dataset.Builder#setAuthentication(android.content.IntentSender) authenticated dataset} * whose presentation displays "Username for some_bank.com" and * "Password for some_bank.com". Then when the user taps one of these options, the service * shows the disclaimer dialog explaining that selecting that option would release the * {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees, * then the service returns an unlocked dataset with the {@code some_bank.com} credentials. * * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the * verifications above, as long as the service can verify the authenticity of the browser app by * checking its signing certificate. */ */ public abstract class AutofillService extends Service { public abstract class AutofillService extends Service { private static final String TAG = "AutofillService"; private static final String TAG = "AutofillService"; Loading Loading @@ -424,7 +494,7 @@ public abstract class AutofillService extends Service { * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)}) * to notify the result of the request. * to notify the result of the request. * * * <p><b>NOTE: </b>to retrieve the actual value of the field, the service should call * <p><b>Note:</b> To retrieve the actual value of the field, the service should call * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls * {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}; if it calls * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no * {@link android.app.assist.AssistStructure.ViewNode#getText()} or other methods, there is no * guarantee such method will return the most recent value of the field. * guarantee such method will return the most recent value of the field. Loading
core/java/android/webkit/WebView.java +12 −0 Original line number Original line Diff line number Diff line Loading @@ -2725,6 +2725,18 @@ public class WebView extends AbsoluteLayout * understood by the {@link android.service.autofill.AutofillService} implementations: * understood by the {@link android.service.autofill.AutofillService} implementations: * * * <ol> * <ol> * <li>Only the HTML nodes inside a {@code FORM} are generated. * <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the * node representing the WebView. * <li>If a web page has multiple {@code FORM}s, only the data for the current form is * represented—if the user taps a field from another form, then the current autofill * context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and * a new context is created for that {@code FORM}. * <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in * the view structure until the user taps a field from a {@code FORM} inside the * {@code IFRAME}, in which case it would be treated the same way as multiple forms described * above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the * {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node. * <li>If the Android SDK provides a similar View, then should be set with the * <li>If the Android SDK provides a similar View, then should be set with the * fully-qualified class name of such view. * fully-qualified class name of such view. * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to * <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to Loading