LuhnChecksumValidator.java revision 0e3e6f8274b9d4c1c9646a1d39aed6a9f8a6a28d
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
17package android.service.autofill;
18
19import static android.view.autofill.Helper.sDebug;
20
21import android.annotation.NonNull;
22import android.annotation.TestApi;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.util.Log;
26import android.view.autofill.AutofillId;
27
28import com.android.internal.util.Preconditions;
29
30/**
31 * Validator that returns {@code true} if the number created by concatenating all given fields
32 * pass a Luhn algorithm checksum. All non-digits are ignored.
33 *
34 * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples.
35 */
36public final class LuhnChecksumValidator extends InternalValidator implements Validator,
37        Parcelable {
38    private static final String TAG = "LuhnChecksumValidator";
39
40    private final AutofillId[] mIds;
41
42    /**
43      * Default constructor.
44      *
45      * @param ids id of fields that comprises the number to be checked.
46      */
47    public LuhnChecksumValidator(@NonNull AutofillId... ids) {
48        mIds = Preconditions.checkArrayElementsNotNull(ids, "ids");
49    }
50
51    /**
52     * Checks if the Luhn checksum is valid.
53     *
54     * @param number The number including the checksum
55     */
56    private static boolean isLuhnChecksumValid(@NonNull String number) {
57        int sum = 0;
58        boolean isDoubled = false;
59
60        for (int i = number.length() - 1; i >= 0; i--) {
61            final int digit = number.charAt(i) - '0';
62            if (digit < 0 || digit > 9) {
63                // Ignore non-digits
64                continue;
65            }
66
67            int addend;
68            if (isDoubled) {
69                addend = digit * 2;
70                if (addend > 9) {
71                    addend -= 9;
72                }
73            } else {
74                addend = digit;
75            }
76            sum += addend;
77            isDoubled = !isDoubled;
78        }
79
80        return sum % 10 == 0;
81    }
82
83    /** @hide */
84    @Override
85    @TestApi
86    public boolean isValid(@NonNull ValueFinder finder) {
87        if (mIds == null || mIds.length == 0) return false;
88
89        final StringBuilder number = new StringBuilder();
90        for (AutofillId id : mIds) {
91            final String partialNumber = finder.findByAutofillId(id);
92            if (partialNumber == null) {
93                if (sDebug) Log.d(TAG, "No partial number for id " + id);
94                return false;
95            }
96            number.append(partialNumber);
97        }
98
99        return isLuhnChecksumValid(number.toString());
100    }
101
102    /////////////////////////////////////
103    // Parcelable "contract" methods. //
104    /////////////////////////////////////
105    @Override
106    public int describeContents() {
107        return 0;
108    }
109
110    @Override
111    public void writeToParcel(Parcel parcel, int flags) {
112        parcel.writeParcelableArray(mIds, flags);
113    }
114
115    public static final Parcelable.Creator<LuhnChecksumValidator> CREATOR =
116            new Parcelable.Creator<LuhnChecksumValidator>() {
117        @Override
118        public LuhnChecksumValidator createFromParcel(Parcel parcel) {
119            return new LuhnChecksumValidator(parcel.readParcelableArray(null, AutofillId.class));
120        }
121
122        @Override
123        public LuhnChecksumValidator[] newArray(int size) {
124            return new LuhnChecksumValidator[size];
125        }
126    };
127}
128