10825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/*
20825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Copyright (C) 2011 The Android Open Source Project
30825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
40825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Licensed under the Apache License, Version 2.0 (the "License");
50825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * you may not use this file except in compliance with the License.
60825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * You may obtain a copy of the License at
70825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
80825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *      http://www.apache.org/licenses/LICENSE-2.0
90825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Unless required by applicable law or agreed to in writing, software
110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * distributed under the License is distributed on an "AS IS" BASIS,
120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * See the License for the specific language governing permissions and
140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * limitations under the License.
150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
170825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepackage com.android.internal.telephony;
180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.ArrayList;
200825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.Iterator;
210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Clients can enable reception of SMS-CB messages for specific ranges of
240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * message identifiers (channels). This class keeps track of the currently
250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * enabled message identifiers and calls abstract methods to update the
260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * radio when the range of enabled message identifiers changes.
270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * An update is a call to {@link #startUpdate} followed by zero or more
290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * calls to {@link #addRange} followed by a call to {@link #finishUpdate}.
300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Calls to {@link #enableRange} and {@link #disableRange} will perform
310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * an incremental update operation if the enabled ranges have changed.
320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * A full update operation (i.e. after a radio reset) can be performed
330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * by a call to {@link #updateRanges}.
340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville *
350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Clients are identified by String (the name associated with the User ID
360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * of the caller) so that a call to remove a range can be mapped to the
370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * client that enabled that range (or else rejected).
380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
390825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic abstract class IntRangeManager {
400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Initial capacity for IntRange clients array list. There will be
430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * few cell broadcast listeners on a typical device, so this can be small.
440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int INITIAL_CLIENTS_ARRAY_SIZE = 4;
460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * One or more clients forming the continuous range [startId, endId].
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * <p>When a client is added, the IntRange may merge with one or more
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * adjacent IntRanges to form a single combined IntRange.
510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * <p>When a client is removed, the IntRange may divide into several
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * non-contiguous IntRanges.
530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private class IntRange {
5522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        int mStartId;
5622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        int mEndId;
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // sorted by earliest start id
5822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        final ArrayList<ClientRange> mClients;
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /**
610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * Create a new IntRange with a single client.
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param startId the first id included in the range
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param endId the last id included in the range
640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param client the client requesting the enabled range
650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        IntRange(int startId, int endId, String client) {
6722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mStartId = startId;
6822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mEndId = endId;
6922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
7022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients.add(new ClientRange(startId, endId, client));
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /**
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * Create a new IntRange for an existing ClientRange.
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param clientRange the initial ClientRange to add
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        IntRange(ClientRange clientRange) {
7822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mStartId = clientRange.mStartId;
7922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mEndId = clientRange.mEndId;
8022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
8122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients.add(clientRange);
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /**
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * Create a new IntRange from an existing IntRange. This is used for
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * removing a ClientRange, because new IntRanges may need to be created
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * for any gaps that open up after the ClientRange is removed. A copy
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * is made of the elements of the original IntRange preceding the element
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * that is being removed. The following elements will be added to this
900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * IntRange or to a new IntRange when a gap is found.
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param intRange the original IntRange to copy elements from
920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param numElements the number of elements to copy from the original
930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        IntRange(IntRange intRange, int numElements) {
9522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mStartId = intRange.mStartId;
9622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mEndId = intRange.mEndId;
9722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients = new ArrayList<ClientRange>(intRange.mClients.size());
980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i=0; i < numElements; i++) {
9922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mClients.add(intRange.mClients.get(i));
1000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        /**
10401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks         * Insert new ClientRange in order by start id, then by end id
1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * <p>If the new ClientRange is known to be sorted before or after the
1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * existing ClientRanges, or at a particular index, it can be added
1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * to the clients array list directly, instead of via this method.
1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * <p>Note that this can be changed from linear to binary search if the
1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * number of clients grows large enough that it would make a difference.
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         * @param range the new ClientRange to insert
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville         */
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        void insert(ClientRange range) {
11322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            int len = mClients.size();
11401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            int insert = -1;
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i=0; i < len; i++) {
11622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                ClientRange nextRange = mClients.get(i);
11722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                if (range.mStartId <= nextRange.mStartId) {
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // ignore duplicate ranges from the same client
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (!range.equals(nextRange)) {
12001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // check if same startId, then order by endId
1214e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        if (range.mStartId == nextRange.mStartId
1224e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                                && range.mEndId > nextRange.mEndId) {
12301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            insert = i + 1;
12401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            if (insert < len) {
12501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // there may be more client following with same startId
12601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // new [1, 5] existing [1, 2] [1, 4] [1, 7]
12701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                continue;
12801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            }
12901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            break;
13001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
13122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        mClients.add(i, range);
1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return;
1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
1350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
13601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            if (insert != -1 && insert < len) {
1374e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                mClients.add(insert, range);
13801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                return;
13901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            }
14022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClients.add(range);    // append to end of list
1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The message id range for a single client.
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private class ClientRange {
14822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        final int mStartId;
14922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        final int mEndId;
15022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        final String mClient;
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ClientRange(int startId, int endId, String client) {
15322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mStartId = startId;
15422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mEndId = endId;
15522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            mClient = client;
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public boolean equals(Object o) {
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (o != null && o instanceof ClientRange) {
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ClientRange other = (ClientRange) o;
16222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                return mStartId == other.mStartId &&
16322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        mEndId == other.mEndId &&
16422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        mClient.equals(other.mClient);
1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;
1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public int hashCode() {
17222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            return (mStartId * 31 + mEndId) * 31 + mClient.hashCode();
1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * List of integer ranges, one per client, sorted by start id.
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ArrayList<IntRange> mRanges = new ArrayList<IntRange>();
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected IntRangeManager() {}
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Enable a range for the specified client and update ranges
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * if necessary. If {@link #finishUpdate} returns failure,
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * false is returned and the range is not added.
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param client the client requesting the enabled range
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public synchronized boolean enableRange(int startId, int endId, String client) {
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int len = mRanges.size();
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // empty range list: add the initial IntRange
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (len == 0) {
19801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            if (tryAddRanges(startId, endId, true)) {
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mRanges.add(new IntRange(startId, endId, client));
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return true;
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;   // failed to update radio
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int startIndex = 0; startIndex < len; startIndex++) {
2070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            IntRange range = mRanges.get(startIndex);
2084e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby            if ((startId) >= range.mStartId && (endId) <= range.mEndId) {
20901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // exact same range:  new [1, 1] existing [1, 1]
21001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // range already enclosed in existing: new [3, 3], [1,3]
21101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // no radio update necessary.
21201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // duplicate "client" check is done in insert, attempt to insert.
21301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                range.insert(new ClientRange(startId, endId, client));
21401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                return true;
2154e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby            } else if ((startId - 1) == range.mEndId) {
21601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [3, x] existing [1, 2]  OR new [2, 2] existing [1, 1]
21701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // found missing link? check if next range can be joined
21801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                int newRangeEndId = endId;
21901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                IntRange nextRange = null;
22001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                if ((startIndex + 1) < len) {
22101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    nextRange = mRanges.get(startIndex + 1);
2224e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                    if ((nextRange.mStartId - 1) <= endId) {
22301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // new [3, x] existing [1, 2] [5, 7] OR  new [2 , 2] existing [1, 1] [3, 5]
2244e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        if (endId <= nextRange.mEndId) {
22501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [3, 6] existing [1, 2] [5, 7]
2264e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                            newRangeEndId = nextRange.mStartId - 1; // need to enable [3, 4]
22701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
22801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    } else {
22901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // mark nextRange to be joined as null.
23001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        nextRange = null;
23101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    }
23201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                }
23301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                if (tryAddRanges(startId, newRangeEndId, true)) {
2344e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                    range.mEndId = endId;
23501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    range.insert(new ClientRange(startId, endId, client));
23601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
23701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // found missing link? check if next range can be joined
23801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (nextRange != null) {
2394e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        if (range.mEndId < nextRange.mEndId) {
24001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [3, 6] existing [1, 2] [5, 10]
2414e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                            range.mEndId = nextRange.mEndId;
24201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
2434e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        range.mClients.addAll(nextRange.mClients);
24401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        mRanges.remove(nextRange);
24501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    }
24601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    return true;
24701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                } else {
24801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    return false;   // failed to update radio
24901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                }
2504e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby            } else if (startId < range.mStartId) {
25101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [1, x] , existing [5, y]
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // test if new range completely precedes this range
2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // note that [1, 4] and [5, 6] coalesce to [1, 6]
25422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                if ((endId + 1) < range.mStartId) {
25501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 3] existing [5, 6] non contiguous case
2560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // insert new int range before previous first range
25701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(startId, endId, true)) {
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.add(startIndex, new IntRange(startId, endId, client));
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
26322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                } else if (endId <= range.mEndId) {
26401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 4] existing [5, 6]  or  new [1, 1] existing [2, 2]
2650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // extend the start of this range
2664e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                    if (tryAddRanges(startId, range.mStartId - 1, true)) {
26722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        range.mStartId = startId;
26822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        range.mClients.add(0, new ClientRange(startId, endId, client));
2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // find last range that can coalesce into the new combined range
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange endRange = mRanges.get(endIndex);
27722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        if ((endId + 1) < endRange.mStartId) {
27801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [14, 15]
2790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // try to add entire new range
28001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            if (tryAddRanges(startId, endId, true)) {
28122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mStartId = startId;
28222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mEndId = endId;
2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // insert new ClientRange before existing ranges
28422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mClients.add(0, new ClientRange(startId, endId, client));
2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // coalesce range with following ranges up to endIndex-1
2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // remove each range after adding its elements, so the index
2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // of the next range to join is always startIndex+1.
2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // i is the index if no elements were removed: we only care
2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // about the number of loop iterations, not the value of i.
2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                int joinIndex = startIndex + 1;
2910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                for (int i = joinIndex; i < endIndex; i++) {
29201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    // new [1, 10] existing [2, 3] [5, 6] [14, 15]
2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    IntRange joinRange = mRanges.get(joinIndex);
29422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                    range.mClients.addAll(joinRange.mClients);
2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    mRanges.remove(joinRange);
2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return false;   // failed to update radio
3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
30122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        } else if (endId <= endRange.mEndId) {
30201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [5, 15]
3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // add range from start id to start of last overlapping range,
3040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // values from endRange.startId to endId are already enabled
3054e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                            if (tryAddRanges(startId, endRange.mStartId - 1, true)) {
30622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mStartId = startId;
30722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mEndId = endRange.mEndId;
3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // insert new ClientRange before existing ranges
30922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                range.mClients.add(0, new ClientRange(startId, endId, client));
3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // coalesce range with following ranges up to endIndex
3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // remove each range after adding its elements, so the index
3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // of the next range to join is always startIndex+1.
3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // i is the index if no elements were removed: we only care
3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // about the number of loop iterations, not the value of i.
3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                int joinIndex = startIndex + 1;
3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                for (int i = joinIndex; i <= endIndex; i++) {
3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    IntRange joinRange = mRanges.get(joinIndex);
31822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                    range.mClients.addAll(joinRange.mClients);
3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    mRanges.remove(joinRange);
3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return false;   // failed to update radio
3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
32801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 10] existing [2, 3]
3290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // endId extends past all existing IntRanges: combine them all together
33001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(startId, endId, true)) {
33122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        range.mStartId = startId;
33222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        range.mEndId = endId;
3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // insert new ClientRange before existing ranges
33422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        range.mClients.add(0, new ClientRange(startId, endId, client));
3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // coalesce range with following ranges up to len-1
3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove each range after adding its elements, so the index
3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // of the next range to join is always startIndex+1.
3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // i is the index if no elements were removed: we only care
3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // about the number of loop iterations, not the value of i.
3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        int joinIndex = startIndex + 1;
3410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        for (int i = joinIndex; i < len; i++) {
34201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [5, 6]
3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            IntRange joinRange = mRanges.get(joinIndex);
34422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            range.mClients.addAll(joinRange.mClients);
3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            mRanges.remove(joinRange);
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
35222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            } else if ((startId + 1) <= range.mEndId) {
35301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [2, x] existing [1, 4]
35422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                if (endId <= range.mEndId) {
35501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 3] existing [1, 4]
3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // completely contained in existing range; no radio changes
3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    range.insert(new ClientRange(startId, endId, client));
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return true;
3590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
36001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 5] existing [1, 4]
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // find last range that can coalesce into the new combined range
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    int endIndex = startIndex;
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    for (int testIndex = startIndex+1; testIndex < len; testIndex++) {
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange testRange = mRanges.get(testIndex);
36522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        if ((endId + 1) < testRange.mStartId) {
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            break;
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            endIndex = testIndex;
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // no adjacent IntRanges to combine
3720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (endIndex == startIndex) {
37301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // new [2, 5] existing [1, 4]
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // add range from range.endId+1 to endId,
3750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // values from startId to range.endId are already enabled
3764e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        if (tryAddRanges(range.mEndId + 1, endId, true)) {
37722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            range.mEndId = endId;
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            range.insert(new ClientRange(startId, endId, client));
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return true;
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return false;   // failed to update radio
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // get last range to coalesce into start range
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    IntRange endRange = mRanges.get(endIndex);
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // Values from startId to range.endId have already been enabled.
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // if endId > endRange.endId, then enable range from range.endId+1 to endId,
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // else enable range from range.endId+1 to endRange.startId-1, because
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // values from endRange.startId to endId have already been added.
39022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    int newRangeEndId = (endId <= endRange.mEndId) ? endRange.mStartId - 1 : endId;
39101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 10] existing [1, 4] [7, 8] OR
39201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 10] existing [1, 4] [7, 15]
3934e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                    if (tryAddRanges(range.mEndId + 1, newRangeEndId, true)) {
3944e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        newRangeEndId = (endId <= endRange.mEndId) ? endRange.mEndId : endId;
3954e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                        range.mEndId = newRangeEndId;
3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // insert new ClientRange in place
3970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.insert(new ClientRange(startId, endId, client));
39801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // coalesce range with following ranges up to endIndex
3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove each range after adding its elements, so the index
4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // of the next range to join is always startIndex+1 (joinIndex).
4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // i is the index if no elements had been removed: we only care
4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // about the number of loop iterations, not the value of i.
4030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        int joinIndex = startIndex + 1;
40401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        for (int i = joinIndex; i <= endIndex; i++) {
4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            IntRange joinRange = mRanges.get(joinIndex);
40622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            range.mClients.addAll(joinRange.mClients);
4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            mRanges.remove(joinRange);
4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
41701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // new [5, 6], existing [1, 3]
4180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // append new range after existing IntRanges
41901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        if (tryAddRanges(startId, endId, true)) {
4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mRanges.add(new IntRange(startId, endId, client));
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return true;
4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return false;   // failed to update radio
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Disable a range for the specified client and update ranges
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * if necessary. If {@link #finishUpdate} returns failure,
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * false is returned and the range is not removed.
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param client the client requesting to disable the range
4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public synchronized boolean disableRange(int startId, int endId, String client) {
4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int len = mRanges.size();
4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i=0; i < len; i++) {
4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            IntRange range = mRanges.get(i);
44222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            if (startId < range.mStartId) {
4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;   // not found
44422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville            } else if (endId <= range.mEndId) {
4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // found the IntRange that encloses the client range, if any
4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // search for it in the clients list
44722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                ArrayList<ClientRange> clients = range.mClients;
4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // handle common case of IntRange containing one ClientRange
4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int crLength = clients.size();
4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (crLength == 1) {
4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ClientRange cr = clients.get(0);
45322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    if (cr.mStartId == startId && cr.mEndId == endId && cr.mClient.equals(client)) {
45401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // mRange contains only what's enabled.
45501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // remove the range from mRange then update the radio
45601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        mRanges.remove(i);
45701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (updateRanges()) {
4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return true;
4590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
46001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // failed to update radio.  insert back the range
46101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.add(i, range);
46201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            return false;
4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // not found
4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // several ClientRanges: remove one, potentially splitting into many IntRanges.
4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Save the original start and end id for the original IntRange
4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // in case the radio update fails and we have to revert it. If the
4720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // update succeeds, we remove the client range and insert the new IntRanges.
47301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // clients are ordered by startId then by endId, so client with largest endId
47401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // can be anywhere.  Need to loop thru to find largestEndId.
4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int largestEndId = Integer.MIN_VALUE;  // largest end identifier found
4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                boolean updateStarted = false;
4770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
47801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // crlength >= 2
4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                for (int crIndex=0; crIndex < crLength; crIndex++) {
4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ClientRange cr = clients.get(crIndex);
48122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                    if (cr.mStartId == startId && cr.mEndId == endId && cr.mClient.equals(client)) {
4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // found the ClientRange to remove, check if it's the last in the list
4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (crIndex == crLength - 1) {
48422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            if (range.mEndId == largestEndId) {
48501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // remove [2, 5] from [1, 7] [2, 5]
4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // no channels to remove from radio; return success
4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                clients.remove(crIndex);
4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
4900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // disable the channels at the end and lower the end id
49101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                clients.remove(crIndex);
4924e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                                range.mEndId = largestEndId;
49301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                if (updateRanges()) {
4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    return true;
4950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                } else {
49601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    clients.add(crIndex, cr);
4974e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                                    range.mEndId = cr.mEndId;
4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    return false;
4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // copy the IntRange so that we can remove elements and modify the
5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // start and end id's in the copy, leaving the original unmodified
5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // until after the radio update succeeds
5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange rangeCopy = new IntRange(range, crIndex);
5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (crIndex == 0) {
5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // removing the first ClientRange, so we may need to increase
5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // the start id of the IntRange.
5110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // We know there are at least two ClientRanges in the list,
5124e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                            // because check for just one ClientRanges case is already handled
5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // so clients.get(1) should always succeed.
51422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            int nextStartId = clients.get(1).mStartId;
51522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            if (nextStartId != range.mStartId) {
5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                updateStarted = true;
51722d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                rangeCopy.mStartId = nextStartId;
5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // init largestEndId
52022d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            largestEndId = clients.get(1).mEndId;
5210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // go through remaining ClientRanges, creating new IntRanges when
5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // there is a gap in the sequence. After radio update succeeds,
5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove the original IntRange and append newRanges to mRanges.
5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // Otherwise, leave the original IntRange in mRanges and return false.
5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        ArrayList<IntRange> newRanges = new ArrayList<IntRange>();
5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange currentRange = rangeCopy;
5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            ClientRange nextCr = clients.get(nextIndex);
53222d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            if (nextCr.mStartId > largestEndId + 1) {
53301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                updateStarted = true;
53422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                currentRange.mEndId = largestEndId;
5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                newRanges.add(currentRange);
5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                currentRange = new IntRange(nextCr);
5370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
5384e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                                if (currentRange.mEndId < nextCr.mEndId) {
5394e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                                    currentRange.mEndId = nextCr.mEndId;
54001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                }
54122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                currentRange.mClients.add(nextCr);
5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
54322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            if (nextCr.mEndId > largestEndId) {
54422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                                largestEndId = nextCr.mEndId;
5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove any channels between largestEndId and endId
5490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (largestEndId < endId) {
55001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            updateStarted = true;
55122d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            currentRange.mEndId = largestEndId;
5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        newRanges.add(currentRange);
5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // replace the original IntRange with newRanges
5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.remove(i);
5570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.addAll(i, newRanges);
55801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (updateStarted && !updateRanges()) {
55901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // failed to update radio.  revert back mRange.
56001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.removeAll(newRanges);
56101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.add(i, range);
56201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            return false;
56301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
56401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // not the ClientRange to remove; save highest end ID seen so far
56822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                        if (cr.mEndId > largestEndId) {
56922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                            largestEndId = cr.mEndId;
5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
5730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return false;   // not found
5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Perform a complete update operation (enable all ranges). Useful
5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * after a radio reset. Calls {@link #startUpdate}, followed by zero or
5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * more calls to {@link #addRange}, followed by {@link #finishUpdate}.
5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean updateRanges() {
5860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        startUpdate();
58701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
58801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        populateAllRanges();
5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return finishUpdate();
5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Enable or disable a single range of message identifiers.
5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param selected true to enable range, false to disable range
5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
5980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
59901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    protected boolean tryAddRanges(int startId, int endId, boolean selected) {
60001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
6010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        startUpdate();
60201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        populateAllRanges();
60301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // This is the new range to be enabled
60401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        addRange(startId, endId, selected); // adds to mConfigList
6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return finishUpdate();
6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns whether the list of ranges is completely empty.
6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if there are no enabled ranges
6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean isEmpty() {
6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return mRanges.isEmpty();
6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
61701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Called when attempting to add a single range of message identifiers
61801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Populate all ranges of message identifiers.
61901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     */
62001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    private void populateAllRanges() {
62101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        Iterator<IntRange> itr = mRanges.iterator();
62201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // Populate all ranges from mRanges
62301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        while (itr.hasNext()) {
62401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            IntRange currRange = (IntRange) itr.next();
6254e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby            addRange(currRange.mStartId, currRange.mEndId, true);
62601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        }
62701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    }
62801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
62901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    /**
63001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Called when attempting to add a single range of message identifiers
63101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Populate all ranges of message identifiers using clients' ranges.
63201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     */
63301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    private void populateAllClientRanges() {
63401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        int len = mRanges.size();
63501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        for (int i = 0; i < len; i++) {
63601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            IntRange range = mRanges.get(i);
63701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
6384e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby            int clientLen = range.mClients.size();
63901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            for (int j=0; j < clientLen; j++) {
6404e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                ClientRange nextRange = range.mClients.get(j);
6414e9ed0410d72180872d59b14cb375c1c2ed94788Jake Hamby                addRange(nextRange.mStartId, nextRange.mEndId, true);
64201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            }
64301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        }
64401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    }
64501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
64601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    /**
6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called when the list of enabled ranges has changed. This will be
6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * followed by zero or more calls to {@link #addRange} followed by
6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * a call to {@link #finishUpdate}.
6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract void startUpdate();
6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called after {@link #startUpdate} to indicate a range of enabled
6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * or disabled values.
6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param selected true to enable range, false to disable range
6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract void addRange(int startId, int endId, boolean selected);
6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called to indicate the end of a range update started by the
6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * previous call to {@link #startUpdate}.
6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract boolean finishUpdate();
6690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
670