Home | History | Annotate | Download | only in autofill
      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.TestApi;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.util.Log;
     26 import android.view.autofill.AutofillId;
     27 
     28 import 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  */
     36 public 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