LuhnChecksumValidator.java revision 4e5ce390399a77d47aa775e9eb7d04b5e8061483
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 Parcelable {
37    private static final String TAG = "LuhnChecksumValidator";
38
39    private final AutofillId[] mIds;
40
41    /**
42      * Default constructor.
43      *
44      * @param ids id of fields that comprises the number to be checked.
45      */
46    public LuhnChecksumValidator(@NonNull AutofillId... ids) {
47        mIds = Preconditions.checkArrayElementsNotNull(ids, "ids");
48    }
49
50    /**
51     * Checks if the Luhn checksum is valid.
52     *
53     * @param number The number including the checksum
54     */
55    private static boolean isLuhnChecksumValid(@NonNull String number) {
56        int sum = 0;
57        boolean isDoubled = false;
58
59        for (int i = number.length() - 1; i >= 0; i--) {
60            final int digit = number.charAt(i) - '0';
61            if (digit < 0 || digit > 9) {
62                // Ignore non-digits
63                continue;
64            }
65
66            int addend;
67            if (isDoubled) {
68                addend = digit * 2;
69                if (addend > 9) {
70                    addend -= 9;
71                }
72            } else {
73                addend = digit;
74            }
75            sum += addend;
76            isDoubled = !isDoubled;
77        }
78
79        return sum % 10 == 0;
80    }
81
82    /** @hide */
83    @Override
84    @TestApi
85    public boolean isValid(@NonNull ValueFinder finder) {
86        if (mIds == null || mIds.length == 0) return false;
87
88        final StringBuilder number = new StringBuilder();
89        for (AutofillId id : mIds) {
90            final String partialNumber = finder.findByAutofillId(id);
91            if (partialNumber == null) {
92                if (sDebug) Log.d(TAG, "No partial number for id " + id);
93                return false;
94            }
95            number.append(partialNumber);
96        }
97
98        return isLuhnChecksumValid(number.toString());
99    }
100
101    /////////////////////////////////////
102    // Parcelable "contract" methods. //
103    /////////////////////////////////////
104    @Override
105    public int describeContents() {
106        return 0;
107    }
108
109    @Override
110    public void writeToParcel(Parcel parcel, int flags) {
111        parcel.writeParcelableArray(mIds, flags);
112    }
113
114    public static final Parcelable.Creator<LuhnChecksumValidator> CREATOR =
115            new Parcelable.Creator<LuhnChecksumValidator>() {
116        @Override
117        public LuhnChecksumValidator createFromParcel(Parcel parcel) {
118            return new LuhnChecksumValidator(parcel.readParcelableArray(null, AutofillId.class));
119        }
120
121        @Override
122        public LuhnChecksumValidator[] newArray(int size) {
123            return new LuhnChecksumValidator[size];
124        }
125    };
126}
127