Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94c8193a authored by Andre Eisenbach's avatar Andre Eisenbach
Browse files

Improve VCARD filter logic and reduce log spam

Remove a stray log message in VCardFilter.applyFilter() function that
leads to high-frequency log spam during phone book synchronization. Also
refactored the logic to avoid multiple loops & loop iterations causing
higher CPU load.

Bug: 18913091
Change-Id: I3c6d3c3373d4a74a930d182ee2641959461b0556
parent a7e922a3
Loading
Loading
Loading
Loading
+77 −287
Original line number Original line Diff line number Diff line
@@ -520,10 +520,8 @@ public class BluetoothPbapVcardManager {


        if (isContacts) {
        if (isContacts) {
            VCardComposer composer = null;
            VCardComposer composer = null;
            FilterVcard vcardfilter= new FilterVcard();
            VCardFilter vcardfilter= new VCardFilter(ignorefilter ? null : filter);
            if (!ignorefilter) {

                vcardfilter.setFilter(filter);
            }
            HandlerForStringBuffer buffer = null;
            HandlerForStringBuffer buffer = null;
            try {
            try {
                // Currently only support Generic Vcard 2.1 and 3.0
                // Currently only support Generic Vcard 2.1 and 3.0
@@ -575,15 +573,11 @@ public class BluetoothPbapVcardManager {
                        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                        return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                    }
                    }
                    if (V) Log.v (TAG , "vCard from composer: " + vcard);
                    if (V) Log.v (TAG , "vCard from composer: " + vcard);
                    if (!ignorefilter) {

                        vcard = vcardfilter.applyFilter(vcard, vcardType21);
                    vcard = vcardfilter.apply(vcard, vcardType21);
                        if (V) Log.v (TAG , "vCard on applying filter: " + vcard);
                    }
                    vcard = StripTelephoneNumber(vcard);
                    vcard = StripTelephoneNumber(vcard);
                    if (V) {

                        Log.v(TAG, "Vcard Entry:");
                    if (V) Log.v (TAG, "vCard after cleanup: " + vcard);
                        Log.v(TAG,vcard);
                    }


                    if (!buffer.onEntryCreated(vcard)) {
                    if (!buffer.onEntryCreated(vcard)) {
                        // onEntryCreate() already emits error.
                        // onEntryCreate() already emits error.
@@ -724,288 +718,84 @@ public class BluetoothPbapVcardManager {
        }
        }
    }
    }


    public class FilterVcard{
    public static class VCardFilter {

        private static enum FilterBit {
        public FilterVcard(){
            //       bit  property    onlyCheckV21  excludeForV21
        };
            FN (       1, "FN",       true,         false),

            PHOTO(     3, "PHOTO",    false,        false),
        private final int FN_BIT = 1;
            BDAY(      4, "BDAY",     false,        false),

            ADR(       5, "ADR",      false,        false),
        private boolean fn = true;
            EMAIL(     8, "EMAIL",    false,        false),

            TITLE(    12, "TITLE",    false,        false),
        private final int PHOTO_BIT = 3;
            ORG(      16, "ORG",      false,        false),

            NOTES(    17, "NOTES",    false,        false),
        private boolean photo = true;
            URL(      20, "URL",      false,        false),

            NICKNAME( 23, "NICKNAME", false,        true);
        //BDAY falls under events
        private final int BDAY_BIT = 4;


        private boolean bday = true;
            public final int pos;
            public final String prop;
            public final boolean onlyCheckV21;
            public final boolean excludeForV21;


        private final int ADR_BIT = 5;
            FilterBit(int pos, String prop, boolean onlyCheckV21, boolean excludeForV21) {

                this.pos = pos;
        private boolean adr = true;
                this.prop = prop;

                this.onlyCheckV21 = onlyCheckV21;
        private final int EMAIL_BIT = 8;
                this.excludeForV21 = excludeForV21;

        private boolean email = true;

        private final int TITLE_BIT = 12;

        private boolean title = true;

        private final int ORG_BIT = 16;

        private boolean org = true;

        private final int NOTES_BIT = 17;

        private boolean notes = true;

        private final int URL_BIT = 20;

        private boolean url = true;

        private final int NICKNAME_BIT = 23;

        private boolean nickname = true;

        public void setFilter(byte[] filter){

           fn = checkbit(FN_BIT, filter);
           photo = checkbit(PHOTO_BIT, filter);
           bday = checkbit(BDAY_BIT, filter);
           adr = checkbit(ADR_BIT, filter);
           email = checkbit(EMAIL_BIT, filter);
           title = checkbit(TITLE_BIT, filter);
           org = checkbit(ORG_BIT, filter);
           notes = checkbit(NOTES_BIT, filter);
           url = checkbit(URL_BIT, filter);
           nickname = checkbit(NICKNAME_BIT, filter);
            }
            }

        private boolean checkbit (int attr_bit, byte[] filter){
            int filterlen = filter.length;
            if( ((filter[filterlen -1 -((int)attr_bit/8)] >> (attr_bit%8)) & 0x01) == 0) {
                return false;
            }
            return true;
        }

        public boolean isPhotoEnabled(){
            return photo;
        }

        private boolean checkValidFilter (String attr) {
            if((attr.startsWith("N:")) || (attr.startsWith("TEL"))
                || (attr.startsWith("VERSION")) || (attr.startsWith("URL"))
                || (attr.startsWith("FN")) || (attr.startsWith("BDAY"))
                || (attr.startsWith("ADR")) || (attr.startsWith("EMAIL"))
                || (attr.startsWith("TITLE")) || (attr.startsWith("ORG"))
                || (attr.startsWith("NOTE")) || (attr.startsWith("NICKNAME"))) {
                return true;
            }
            return false;
        }
        }


        public String applyFilter ( String vCard, boolean vCardType21){
        private static final String SEPARATOR = System.getProperty("line.separator");
            String attr [] = vCard.split(System.getProperty("line.separator"));
        private final byte[] filter;
            String filteredVcard = "";


            //FN is not the mandatory field in 2.1 vCard
        private boolean isFilteredOut(FilterBit bit, boolean vCardType21) {
            if(((!fn) && (vCardType21)) && (vCard.contains("FN"))) {
            final int offset = (bit.pos / 8) + 1;
                for (int i=0; i < attr.length; i++) {
            final int bit_pos = bit.pos % 8;
                    if(attr[i].startsWith("FN")){
            if (!vCardType21 && bit.onlyCheckV21) return false;
                        attr[i] = "";
            if (vCardType21 && bit.excludeForV21) return true;
                        /** Remove multiline Content, if any */
            if (filter == null || offset >= filter.length) return false;
                        /** End traversal before END:VCARD */
            return ((filter[filter.length - offset] >> bit_pos) & 0x01) != 0;
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }

          //NOTE: No need to check photo, we already refrained it if it is not set in the filter
            if((!bday) && (vCard.contains("BDAY"))) {
                for (int i=0; i < attr.length; i++) {
                    if(attr[i].startsWith("BDAY")){
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }

            if((!adr) && (vCard.contains("ADR"))) {
                for (int i=0; i < attr.length; i++) {
                    if(attr[i].startsWith("ADR")){
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
        }
        }


            if((!email) && (vCard.contains("EMAIL"))) {
        VCardFilter(byte[] filter) {
                for (int i=0; i < attr.length; i++) {
            this.filter = filter;
                    if(attr[i].startsWith("EMAIL")){
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
        }
        }


            if((!title) && (vCard.contains("TITLE"))) {
        public boolean isPhotoEnabled() {
                for (int i=0; i < attr.length; i++) {
            return !isFilteredOut(FilterBit.PHOTO, false);
                    if(attr[i].startsWith("TITLE")){
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
        }
        }


            if((!org) && (vCard.contains("ORG"))) {
        public String apply(String vCard, boolean vCardType21){
                for (int i=0; i < attr.length; i++) {
            if (filter == null) return vCard;
                    if(attr[i].startsWith("ORG")){
            String lines[] = vCard.split(SEPARATOR);
                        attr[i] = "";
            StringBuilder filteredVCard = new StringBuilder();
                        /** Remove multiline Content, if any */
            boolean filteredOut = false;
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }


            if((!notes) && (vCard.contains("NOTE"))) {
            for (String line : lines) {
                for (int i=0; i < attr.length; i++) {
                // Check whether the current property is changing (ignoring multi-line properties)
                    if(attr[i].startsWith("NOTE")){
                // and determine if the current property is filtered in.
                        attr[i] = "";
                if (!Character.isWhitespace(line.charAt(0)) && !line.startsWith("=")) {
                        /** Remove multiline Content, if any */
                    String currentProp = line.split("[;:]")[0];
                        /** End traversal before END:VCARD */
                    filteredOut = false;
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }
            /*Nickname is not supported in 2.1 version.
             *Android still ads it for 2.1 with nickname mentioned in lower case, and therefore
             *we need to check for both cases.
             */
            if(((!nickname) || (vCardType21)) && (vCard.contains("NICKNAME"))) {
                for (int i=0; i < attr.length; i++) {
                    if(attr[i].startsWith("NICKNAME")){
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                                break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }


            if((!url) && (vCard.contains("URL"))) {
                    for (FilterBit bit : FilterBit.values()) {
                for (int i=0; i < attr.length; i++) {
                        if (bit.prop.equals(currentProp)) {
                    if(attr[i].startsWith("URL")){
                            filteredOut = isFilteredOut(bit, vCardType21);
                        attr[i] = "";
                        /** Remove multiline Content, if any */
                        /** End traversal before END:VCARD */
                        for (int j = i+1; j < attr.length - 1; j++) {
                            if (checkValidFilter(attr[j])) {
                            break;
                            break;
                            } else {
                                /** Continuation of above attribute, remove */
                                attr[j] = "";
                            }
                        }
                    }
                }
            }
            /*Since PBAP does not have filter bit for IM and SIP,
             *removing them by default.
            */
            if(vCard.toUpperCase().contains("IM")) {
                for (int i=0; i < attr.length; i++) {
                    if(attr[i].toUpperCase().contains("IM")){
                        vCard = vCard.replace(attr[i] + "\n", "");
                    }
                        }
                        }
                    }
                    }


            if(vCard.toUpperCase().contains("SIP")) {
                    // Since PBAP does not have filter bits for IM and SIP,
                for (int i=0; i < attr.length; i++) {
                    // exclude them by default. Easiest way is to exclude all
                    if(attr[i].toUpperCase().contains("SIP")){
                    // X- fields....
                        vCard = vCard.replace(attr[i] + "\n", "");
                    if (currentProp.startsWith("X-")) filteredOut = true;
                    }
                }
                }
            }

            Log.v(TAG, "Tokens after applying filter: ");


            for (int i=0; i < attr.length; i++) {
                // Build filtered vCard
                if(!attr[i].equals("")){
                if (!filteredOut) filteredVCard.append(line + SEPARATOR);
                    filteredVcard = filteredVcard.concat(attr[i] + "\n");
                }
            }
            }


            return filteredVcard;
            return filteredVCard.toString();
        }
        }
    }
    }
}
}