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

Commit 27d5bbc2 authored by Sal Savage's avatar Sal Savage
Browse files

Create PbapApplicationParameters to abstract request params

Bug: 365626536
Flag: EXEMPT, mechanical refactor, no logic change + tests
Test: atest com.android.bluetooth.pbapclient
Test: m com.android.btservices
Change-Id: Icd3ea5ef9933a4f698303a664ff1ad92f1543934
parent 4f2d3c8d
Loading
Loading
Loading
Loading
+145 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.bluetooth.pbapclient;

public class PbapApplicationParameters {
    private static final String TAG = PbapApplicationParameters.class.getSimpleName();

    // Max size for a phonebook, which determines the max size of a batch and an offset. This comes
    // from the fact that each field is 2 bytes -> 16 bits -> [0, 65535] (i.e, inclusive)
    public static final int MAX_PHONEBOOK_SIZE = 65535;

    // Application Parameter Header keys (PBAP 1.2.3, Section 6.2.1)
    public static final byte OAP_ORDER = 0x01;
    public static final byte OAP_SEARCH_VALUE = 0x02;
    public static final byte OAP_SEARCH_PROPERTY = 0x03;
    public static final byte OAP_MAX_LIST_COUNT = 0x04;
    public static final byte OAP_LIST_START_OFFSET = 0x05;
    public static final byte OAP_PROPERTY_SELECTOR = 0x06;
    public static final byte OAP_FORMAT = 0x07;
    public static final byte OAP_PHONEBOOK_SIZE = 0x08;
    public static final byte OAP_NEW_MISSED_CALLS = 0x09;
    public static final byte OAP_PRIMARY_FOLDER_VERSION = 0x0A;
    public static final byte OAP_SECONDARY_FOLDER_VERSION = 0x0B;
    public static final byte OAP_VCARD_SELECTOR = 0x0C;
    public static final byte OAP_DATABASE_IDENTIFIER = 0x0D;
    public static final byte OAP_VCARD_SELECTOR_OPERATOR = 0x0E;
    public static final byte OAP_RESET_NEW_MISSED_CALLS = 0x0F;
    public static final byte OAP_PBAP_SUPPORTED_FEATURES = 0x10;

    // Property Selector "filter" constants, PBAP 1.2.3, section 5.1.4.1
    public static final long PROPERTIES_ALL = 0;
    public static final long PROPERTY_VERSION = 1 << 0;
    public static final long PROPERTY_FN = 1 << 1;
    public static final long PROPERTY_N = 1 << 2;
    public static final long PROPERTY_PHOTO = 1 << 3;
    public static final long PROPERTY_ADR = 1 << 5;
    public static final long PROPERTY_TEL = 1 << 7;
    public static final long PROPERTY_EMAIL = 1 << 8;
    public static final long PROPERTY_NICKNAME = 1 << 23;

    // MaxListCount field, PBAP 1.2.3, Section 5.3.4.4: "0" signifies to the PSE that the PCE is
    // requesting the number of indexes in the phonebook of interest that are actually used
    // (i.e. indexes that correspond to non-NULL entries). Using this causes other parameters to be
    // ignored. Only metadata will be returned, like the size.
    public static final int RETURN_SIZE_ONLY = 0;

    private final long mProperties; // 64-bit property selector bit field, 8 bytes
    private final byte mFormat; // Vcard format, 0 or 1, 1 byte, From PbapVcardList object
    private final int mMaxListCount; // The total number of items to fetch, for batching, 2 bytes
    private final int mListStartOffset; // The item index to start at, for batching, 2 bytes

    PbapApplicationParameters(long properties, byte format, int maxListCount, int listStartOffset) {
        if (maxListCount < 0 || maxListCount > MAX_PHONEBOOK_SIZE) {
            throw new IllegalArgumentException("maxListCount should be [0, 65535]");
        }
        if (listStartOffset < 0 || listStartOffset > MAX_PHONEBOOK_SIZE) {
            throw new IllegalArgumentException("listStartOffset should be [0, 65535]");
        }
        if (format != PbapPhonebook.FORMAT_VCARD_21 && format != PbapPhonebook.FORMAT_VCARD_30) {
            throw new IllegalArgumentException("VCard format must be 2.1 or 3.0");
        }

        mProperties = properties;
        mFormat = format;
        mMaxListCount = maxListCount;
        mListStartOffset = listStartOffset;
    }

    public long getPropertySelectorMask() {
        return mProperties;
    }

    public byte getVcardFormat() {
        return mFormat;
    }

    public int getMaxListCount() {
        return mMaxListCount;
    }

    public int getListStartOffset() {
        return mListStartOffset;
    }

    public static String propertiesToString(long properties) {
        if (properties == PROPERTIES_ALL) {
            return "PROPERTIES_ALL";
        }

        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if ((properties & PROPERTY_VERSION) != 0) {
            sb.append("PROPERTY_VERSION ");
        }
        if ((properties & PROPERTY_FN) != 0) {
            sb.append("PROPERTY_FN ");
        }
        if ((properties & PROPERTY_N) != 0) {
            sb.append("PROPERTY_N ");
        }
        if ((properties & PROPERTY_PHOTO) != 0) {
            sb.append("PROPERTY_PHOTO ");
        }
        if ((properties & PROPERTY_ADR) != 0) {
            sb.append("PROPERTY_ADR ");
        }
        if ((properties & PROPERTY_TEL) != 0) {
            sb.append("PROPERTY_TEL ");
        }
        if ((properties & PROPERTY_EMAIL) != 0) {
            sb.append("PROPERTY_EMAIL ");
        }
        if ((properties & PROPERTY_NICKNAME) != 0) {
            sb.append("PROPERTY_NICKNAME ");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");
        return sb.toString();
    }

    @Override
    public String toString() {
        return "<"
                + TAG
                + (" properties=" + propertiesToString(getPropertySelectorMask()))
                + (" format=" + getVcardFormat())
                + (" maxListCount=" + getMaxListCount())
                + (" listStartOffset=" + getListStartOffset())
                + ">";
    }
}
+29 −37
Original line number Diff line number Diff line
@@ -57,10 +57,6 @@ class PbapClientConnectionHandler extends Handler {
    // progress when Bluetooth stack is torn down.
    private static final int DEFAULT_BATCH_SIZE = 250;

    // Upper limit on the indices of the vcf cards/entries, inclusive,
    // i.e., valid indices are [0, 1, ... , UPPER_LIMIT]
    private static final int UPPER_LIMIT = 65535;

    static final int MSG_CONNECT = 1;
    static final int MSG_DISCONNECT = 2;
    static final int MSG_DOWNLOAD = 3;
@@ -90,26 +86,8 @@ class PbapClientConnectionHandler extends Handler {
    private static final int PBAP_FEATURE_DEFAULT_IMAGE_FORMAT = 0x00000200;
    private static final int PBAP_FEATURE_DOWNLOADING = 0x00000001;

    private static final long PBAP_FILTER_VERSION = 1 << 0;
    private static final long PBAP_FILTER_FN = 1 << 1;
    private static final long PBAP_FILTER_N = 1 << 2;
    private static final long PBAP_FILTER_PHOTO = 1 << 3;
    private static final long PBAP_FILTER_ADR = 1 << 5;
    private static final long PBAP_FILTER_TEL = 1 << 7;
    private static final long PBAP_FILTER_EMAIL = 1 << 8;
    private static final long PBAP_FILTER_NICKNAME = 1 << 23;

    private static final int PBAP_SUPPORTED_FEATURE =
            PBAP_FEATURE_DEFAULT_IMAGE_FORMAT | PBAP_FEATURE_DOWNLOADING;
    private static final long PBAP_REQUESTED_FIELDS =
            PBAP_FILTER_VERSION
                    | PBAP_FILTER_FN
                    | PBAP_FILTER_N
                    | PBAP_FILTER_PHOTO
                    | PBAP_FILTER_ADR
                    | PBAP_FILTER_EMAIL
                    | PBAP_FILTER_TEL
                    | PBAP_FILTER_NICKNAME;

    @VisibleForTesting static final int L2CAP_INVALID_PSM = -1;

@@ -322,7 +300,7 @@ class PbapClientConnectionHandler extends Handler {

                if (mPseRec.getProfileVersion() >= PBAP_V1_2) {
                    oap.add(
                            PbapClientRequest.OAP_TAGID_PBAP_SUPPORTED_FEATURES,
                            PbapApplicationParameters.OAP_PBAP_SUPPORTED_FEATURES,
                            PBAP_SUPPORTED_FEATURE);
                }

@@ -369,9 +347,15 @@ class PbapClientConnectionHandler extends Handler {
            PhonebookPullRequest processor =
                    new PhonebookPullRequest(mPbapClientStateMachine.getContext());

            PbapApplicationParameters params =
                    new PbapApplicationParameters(
                            PbapApplicationParameters.PROPERTIES_ALL,
                            /* format, unused */ (byte) 0,
                            PbapApplicationParameters.RETURN_SIZE_ONLY,
                            /* list start offeset, start from beginning */ 0);

            // Download contacts in batches of size DEFAULT_BATCH_SIZE
            RequestPullPhoneBookSize requestPbSize =
                    new RequestPullPhoneBookSize(path, PBAP_REQUESTED_FIELDS);
            RequestPullPhoneBookSize requestPbSize = new RequestPullPhoneBookSize(path, params);
            requestPbSize.execute(mObexSession);

            int numberOfContactsRemaining = requestPbSize.getSize();
@@ -385,19 +369,21 @@ class PbapClientConnectionHandler extends Handler {
                numberOfContactsRemaining -= 1;
            }

            while ((numberOfContactsRemaining > 0) && (startOffset <= UPPER_LIMIT)) {
            while ((numberOfContactsRemaining > 0)
                    && (startOffset <= PbapApplicationParameters.MAX_PHONEBOOK_SIZE)) {
                int numberOfContactsToDownload =
                        Math.min(
                                Math.min(DEFAULT_BATCH_SIZE, numberOfContactsRemaining),
                                UPPER_LIMIT - startOffset + 1);
                RequestPullPhoneBook request =
                        new RequestPullPhoneBook(
                                path,
                                PBAP_REQUESTED_FIELDS,
                                PbapApplicationParameters.MAX_PHONEBOOK_SIZE - startOffset + 1);

                params =
                        new PbapApplicationParameters(
                                PbapApplicationParameters.PROPERTIES_ALL,
                                PbapPhonebook.FORMAT_VCARD_30,
                                numberOfContactsToDownload,
                                startOffset,
                                mAccount);
                                startOffset);

                RequestPullPhoneBook request = new RequestPullPhoneBook(path, params, mAccount);
                request.execute(mObexSession);
                List<VCardEntry> vcards = request.getList();
                if (PbapPhonebook.FAVORITES_PATH.equals(path)) {
@@ -412,7 +398,8 @@ class PbapClientConnectionHandler extends Handler {
                startOffset += numberOfContactsToDownload;
                numberOfContactsRemaining -= numberOfContactsToDownload;
            }
            if ((startOffset > UPPER_LIMIT) && (numberOfContactsRemaining > 0)) {
            if ((startOffset > PbapApplicationParameters.MAX_PHONEBOOK_SIZE)
                    && (numberOfContactsRemaining > 0)) {
                Log.w(TAG, "Download contacts incomplete, index exceeded upper limit.");
            }
        } catch (IOException e) {
@@ -425,9 +412,14 @@ class PbapClientConnectionHandler extends Handler {
    @VisibleForTesting
    void downloadCallLog(String path, Map<String, Integer> callCounter) {
        try {
            RequestPullPhoneBook request =
                    new RequestPullPhoneBook(
                            path, 0, PbapPhonebook.FORMAT_VCARD_30, 0, 0, mAccount);
            PbapApplicationParameters params =
                    new PbapApplicationParameters(
                            /* properties, unused for call logs */ 0,
                            PbapPhonebook.FORMAT_VCARD_30,
                            0,
                            0);

            RequestPullPhoneBook request = new RequestPullPhoneBook(path, params, mAccount);
            request.execute(mObexSession);
            CallLogPullRequest processor =
                    new CallLogPullRequest(
+0 −11
Original line number Diff line number Diff line
@@ -29,17 +29,6 @@ import java.io.InputStream;
abstract class PbapClientRequest {
    static final String TAG = PbapClientRequest.class.getSimpleName();

    protected static final byte OAP_TAGID_ORDER = 0x01;
    protected static final byte OAP_TAGID_SEARCH_VALUE = 0x02;
    protected static final byte OAP_TAGID_SEARCH_ATTRIBUTE = 0x03;
    protected static final byte OAP_TAGID_MAX_LIST_COUNT = 0x04;
    protected static final byte OAP_TAGID_LIST_START_OFFSET = 0x05;
    protected static final byte OAP_TAGID_PROPERTY_SELECTOR = 0x06;
    protected static final byte OAP_TAGID_FORMAT = 0x07;
    protected static final byte OAP_TAGID_PHONEBOOK_SIZE = 0x08;
    protected static final byte OAP_TAGID_NEW_MISSED_CALLS = 0x09;
    protected static final byte OAP_TAGID_PBAP_SUPPORTED_FEATURES = 0x10;

    protected HeaderSet mHeaderSet;

    protected int mResponseCode;
+15 −30
Original line number Diff line number Diff line
@@ -40,54 +40,39 @@ final class RequestPullPhoneBook extends PbapClientRequest {

    private PbapPhonebook mResponse;

    RequestPullPhoneBook(
            String phonebook,
            long propertySelector,
            byte format,
            int maxListCount,
            int listStartOffset,
            Account account) {

        if (format != PbapPhonebook.FORMAT_VCARD_21 && format != PbapPhonebook.FORMAT_VCARD_30) {
            throw new IllegalArgumentException("Format should be v2.1 or v3.0");
        }

        if (maxListCount < 0 || maxListCount > 65535) {
            throw new IllegalArgumentException("maxListCount should be [0..65535]");
        }

        if (listStartOffset < 0 || listStartOffset > 65535) {
            throw new IllegalArgumentException("listStartOffset should be [0..65535]");
        }

    RequestPullPhoneBook(String phonebook, PbapApplicationParameters params, Account account) {
        mPhonebook = phonebook;
        mFormat = format;
        mMaxListCount = maxListCount;
        mListStartOffset = listStartOffset;
        mFormat = params.getVcardFormat();
        mMaxListCount = params.getMaxListCount();
        mListStartOffset = params.getListStartOffset();
        mAccount = account;

        long properties = params.getPropertySelectorMask();

        mHeaderSet.setHeader(HeaderSet.NAME, phonebook);
        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);

        ObexAppParameters oap = new ObexAppParameters();

        oap.add(OAP_TAGID_FORMAT, format);
        oap.add(PbapApplicationParameters.OAP_FORMAT, mFormat);

        if (propertySelector != 0) {
            oap.add(OAP_TAGID_PROPERTY_SELECTOR, propertySelector);
        if (properties != 0) {
            oap.add(PbapApplicationParameters.OAP_PROPERTY_SELECTOR, properties);
        }

        if (listStartOffset > 0) {
            oap.add(OAP_TAGID_LIST_START_OFFSET, (short) listStartOffset);
        if (mListStartOffset > 0) {
            oap.add(PbapApplicationParameters.OAP_LIST_START_OFFSET, (short) mListStartOffset);
        }

        // maxListCount == 0 indicates to fetch all, in which case we set it to the upper bound
        // Note that Java has no unsigned types. To capture an unsigned value in the range [0, 2^16)
        // we need to use an int and cast to a short (2 bytes). This packs the bits we want.
        if (mMaxListCount > 0) {
            oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) mMaxListCount);
            oap.add(PbapApplicationParameters.OAP_MAX_LIST_COUNT, (short) mMaxListCount);
        } else {
            oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 65535);
            oap.add(
                    PbapApplicationParameters.OAP_MAX_LIST_COUNT,
                    (short) PbapApplicationParameters.MAX_PHONEBOOK_SIZE);
        }

        oap.addToHeaderSet(mHeaderSet);
+12 −15
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.bluetooth.pbapclient;

import android.util.Log;

import com.android.bluetooth.ObexAppParameters;
import com.android.obex.HeaderSet;

@@ -26,33 +24,32 @@ final class RequestPullPhoneBookSize extends PbapClientRequest {

    private static final String TYPE = "x-bt/phonebook";

    private int mSize;

    RequestPullPhoneBookSize(String pbName, long filter) {
        mHeaderSet.setHeader(HeaderSet.NAME, pbName);
    private int mSize = -1;

    RequestPullPhoneBookSize(String phonebook, PbapApplicationParameters params) {
        mHeaderSet.setHeader(HeaderSet.NAME, phonebook);
        mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);

        ObexAppParameters oap = new ObexAppParameters();
        // Set MaxListCount in the request to 0 to get PhonebookSize in the response.
        // If a vCardSelector is present in the request, then the result shall
        // contain the number of items that satisfy the selector’s criteria.
        // See PBAP v1.2.3, Sec. 5.1.4.5.
        oap.add(OAP_TAGID_MAX_LIST_COUNT, (short) 0);
        if (filter != 0) {
            oap.add(OAP_TAGID_PROPERTY_SELECTOR, filter);
        ObexAppParameters oap = new ObexAppParameters();
        oap.add(PbapApplicationParameters.OAP_MAX_LIST_COUNT, (short) 0);

        // Otherwise, listen to the property selector criteria passed in and ignore the rest
        long properties = params.getPropertySelectorMask();
        if (properties != PbapApplicationParameters.PROPERTIES_ALL) {
            oap.add(PbapApplicationParameters.OAP_PROPERTY_SELECTOR, properties);
        }
        oap.addToHeaderSet(mHeaderSet);
    }

    @Override
    protected void readResponseHeaders(HeaderSet headerset) {
        Log.v(TAG, "readResponseHeaders");

        ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);

        if (oap.exists(OAP_TAGID_PHONEBOOK_SIZE)) {
            mSize = oap.getShort(OAP_TAGID_PHONEBOOK_SIZE);
        if (oap.exists(PbapApplicationParameters.OAP_PHONEBOOK_SIZE)) {
            mSize = oap.getShort(PbapApplicationParameters.OAP_PHONEBOOK_SIZE);
        }
    }

Loading