1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.autofill;
18 
19 import static android.view.autofill.Helper.sDebug;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.TestApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.Slog;
27 import android.view.autofill.AutofillValue;
28 
29 import java.util.Objects;
30 import java.util.regex.Matcher;
31 import java.util.regex.Pattern;
32 
33 /**
34  * Sanitizes a text {@link AutofillValue} using a regular expression (regex) substitution.
35  *
36  * <p>For example, to remove spaces from groups of 4-digits in a credit card:
37  *
38  * <pre class="prettyprint">
39  * new TextValueSanitizer(Pattern.compile("^(\\d{4})\\s?(\\d{4})\\s?(\\d{4})\\s?(\\d{4})$"),
40  *     "$1$2$3$4")
41  * </pre>
42  */
43 public final class TextValueSanitizer extends InternalSanitizer implements
44         Sanitizer, Parcelable {
45     private static final String TAG = "TextValueSanitizer";
46 
47     private final Pattern mRegex;
48     private final String mSubst;
49 
50     /**
51      * Default constructor.
52      *
53      * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
54      * are used to substitute parts of the {@link AutofillValue#getTextValue() text value}.
55      * @param subst the string that substitutes the matched regex, using {@code $} for
56      * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
57      */
TextValueSanitizer(@onNull Pattern regex, @NonNull String subst)58     public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
59         mRegex = Objects.requireNonNull(regex);
60         mSubst = Objects.requireNonNull(subst);
61     }
62 
63     /** @hide */
64     @Override
65     @TestApi
66     @Nullable
sanitize(@onNull AutofillValue value)67     public AutofillValue sanitize(@NonNull AutofillValue value) {
68         if (value == null) {
69             Slog.w(TAG, "sanitize() called with null value");
70             return null;
71         }
72         if (!value.isText()) {
73             if (sDebug) Slog.d(TAG, "sanitize() called with non-text value: " + value);
74             return null;
75         }
76 
77         final CharSequence text = value.getTextValue();
78 
79         try {
80             final Matcher matcher = mRegex.matcher(text);
81             if (!matcher.matches()) {
82                 if (sDebug) Slog.d(TAG, "sanitize(): " + mRegex + " failed for " + value);
83                 return null;
84             }
85 
86             final CharSequence sanitized = matcher.replaceAll(mSubst);
87             return AutofillValue.forText(sanitized);
88         } catch (Exception e) {
89             Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
90             return null;
91         }
92     }
93 
94     /////////////////////////////////////
95     // Object "contract" methods. //
96     /////////////////////////////////////
97     @Override
toString()98     public String toString() {
99         if (!sDebug) return super.toString();
100 
101         return "TextValueSanitizer: [regex=" + mRegex + ", subst=" + mSubst + "]";
102     }
103 
104     /////////////////////////////////////
105     // Parcelable "contract" methods. //
106     /////////////////////////////////////
107     @Override
describeContents()108     public int describeContents() {
109         return 0;
110     }
111 
112     @Override
writeToParcel(Parcel parcel, int flags)113     public void writeToParcel(Parcel parcel, int flags) {
114         parcel.writeSerializable(mRegex);
115         parcel.writeString(mSubst);
116     }
117 
118     public static final @android.annotation.NonNull Parcelable.Creator<TextValueSanitizer> CREATOR =
119             new Parcelable.Creator<TextValueSanitizer>() {
120         @Override
121         public TextValueSanitizer createFromParcel(Parcel parcel) {
122             return new TextValueSanitizer((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class), parcel.readString());
123         }
124 
125         @Override
126         public TextValueSanitizer[] newArray(int size) {
127             return new TextValueSanitizer[size];
128         }
129     };
130 }
131