IntRangeManager.java revision 01fdbd3285be1a8ba2143a4bc11a0f5065bb68d0
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 {
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int startId;
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int endId;
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // sorted by earliest start id
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final ArrayList<ClientRange> clients;
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) {
670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.startId = startId;
680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.endId = endId;
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            clients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            clients.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) {
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            startId = clientRange.startId;
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            endId = clientRange.endId;
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            clients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            clients.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) {
950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.startId = intRange.startId;
960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.endId = intRange.endId;
970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.clients = new ArrayList<ClientRange>(intRange.clients.size());
980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i=0; i < numElements; i++) {
990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                this.clients.add(intRange.clients.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) {
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int len = clients.size();
11401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            int insert = -1;
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            for (int i=0; i < len; i++) {
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ClientRange nextRange = clients.get(i);
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (range.startId <= nextRange.startId) {
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
12101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (range.startId == nextRange.startId && range.endId > nextRange.endId) {
12201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            insert = i + 1;
12301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            if (insert < len) {
12401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // there may be more client following with same startId
12501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // new [1, 5] existing [1, 2] [1, 4] [1, 7]
12601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                continue;
12701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            }
12801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            break;
12901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
1300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        clients.add(i, range);
1310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
1320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return;
1330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
1340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
13501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            if (insert != -1 && insert < len) {
13601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                clients.add(insert, range);
13701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                return;
13801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            }
1390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            clients.add(range);    // append to end of list
1400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * The message id range for a single client.
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private class ClientRange {
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int startId;
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final int endId;
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        final String client;
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        ClientRange(int startId, int endId, String client) {
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.startId = startId;
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.endId = endId;
1540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            this.client = client;
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public boolean equals(Object o) {
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (o != null && o instanceof ClientRange) {
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ClientRange other = (ClientRange) o;
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return startId == other.startId &&
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        endId == other.endId &&
1630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        client.equals(other.client);
1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
1650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;
1660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        @Override
1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        public int hashCode() {
1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return (startId * 31 + endId) * 31 + client.hashCode();
1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * List of integer ranges, one per client, sorted by start id.
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private ArrayList<IntRange> mRanges = new ArrayList<IntRange>();
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected IntRangeManager() {}
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Enable a range for the specified client and update ranges
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * if necessary. If {@link #finishUpdate} returns failure,
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * false is returned and the range is not added.
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param client the client requesting the enabled range
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public synchronized boolean enableRange(int startId, int endId, String client) {
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int len = mRanges.size();
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // empty range list: add the initial IntRange
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if (len == 0) {
19701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            if (tryAddRanges(startId, endId, true)) {
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mRanges.add(new IntRange(startId, endId, client));
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return true;
2000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else {
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;   // failed to update radio
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int startIndex = 0; startIndex < len; startIndex++) {
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            IntRange range = mRanges.get(startIndex);
20701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            if ((startId) >= range.startId && (endId) <= range.endId) {
20801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // exact same range:  new [1, 1] existing [1, 1]
20901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // range already enclosed in existing: new [3, 3], [1,3]
21001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // no radio update necessary.
21101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // duplicate "client" check is done in insert, attempt to insert.
21201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                range.insert(new ClientRange(startId, endId, client));
21301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                return true;
21401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            } else if ((startId - 1) == range.endId) {
21501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [3, x] existing [1, 2]  OR new [2, 2] existing [1, 1]
21601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // found missing link? check if next range can be joined
21701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                int newRangeEndId = endId;
21801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                IntRange nextRange = null;
21901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                if ((startIndex + 1) < len) {
22001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    nextRange = mRanges.get(startIndex + 1);
22101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if ((nextRange.startId - 1) <= endId) {
22201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // new [3, x] existing [1, 2] [5, 7] OR  new [2 , 2] existing [1, 1] [3, 5]
22301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (endId <= nextRange.endId) {
22401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [3, 6] existing [1, 2] [5, 7]
22501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            newRangeEndId = nextRange.startId - 1; // need to enable [3, 4]
22601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
22701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    } else {
22801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // mark nextRange to be joined as null.
22901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        nextRange = null;
23001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    }
23101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                }
23201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                if (tryAddRanges(startId, newRangeEndId, true)) {
23301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    range.endId = endId;
23401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    range.insert(new ClientRange(startId, endId, client));
23501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
23601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // found missing link? check if next range can be joined
23701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (nextRange != null) {
23801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (range.endId < nextRange.endId) {
23901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [3, 6] existing [1, 2] [5, 10]
24001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            range.endId = nextRange.endId;
24101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
24201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        range.clients.addAll(nextRange.clients);
24301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        mRanges.remove(nextRange);
24401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    }
24501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    return true;
24601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                } else {
24701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    return false;   // failed to update radio
24801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                }
24901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            } else if (startId < range.startId) {
25001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [1, x] , existing [5, y]
2510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // test if new range completely precedes this range
2520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // note that [1, 4] and [5, 6] coalesce to [1, 6]
2530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if ((endId + 1) < range.startId) {
25401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 3] existing [5, 6] non contiguous case
2550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // insert new int range before previous first range
25601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(startId, endId, true)) {
2570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.add(startIndex, new IntRange(startId, endId, client));
2580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
2590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
2600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
2610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
2620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else if (endId <= range.endId) {
26301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 4] existing [5, 6]  or  new [1, 1] existing [2, 2]
2640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // extend the start of this range
26501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(startId, range.startId - 1, true)) {
2660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.startId = startId;
2670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.clients.add(0, new ClientRange(startId, endId, client));
2680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
2690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
2700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
2710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
2720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
2730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // find last range that can coalesce into the new combined range
2740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
2750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange endRange = mRanges.get(endIndex);
2760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if ((endId + 1) < endRange.startId) {
27701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [14, 15]
2780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // try to add entire new range
27901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            if (tryAddRanges(startId, endId, true)) {
2800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.startId = startId;
2810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.endId = endId;
2820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // insert new ClientRange before existing ranges
2830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.clients.add(0, new ClientRange(startId, endId, client));
2840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // coalesce range with following ranges up to endIndex-1
2850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // remove each range after adding its elements, so the index
2860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // of the next range to join is always startIndex+1.
2870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // i is the index if no elements were removed: we only care
2880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // about the number of loop iterations, not the value of i.
2890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                int joinIndex = startIndex + 1;
2900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                for (int i = joinIndex; i < endIndex; i++) {
29101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    // new [1, 10] existing [2, 3] [5, 6] [14, 15]
2920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    IntRange joinRange = mRanges.get(joinIndex);
2930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    range.clients.addAll(joinRange.clients);
2940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    mRanges.remove(joinRange);
2950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
2960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
2970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
2980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return false;   // failed to update radio
2990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
3000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else if (endId <= endRange.endId) {
30101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [5, 15]
3020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // add range from start id to start of last overlapping range,
3030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // values from endRange.startId to endId are already enabled
30401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            if (tryAddRanges(startId, endRange.startId - 1, true)) {
3050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.startId = startId;
3060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.endId = endRange.endId;
3070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // insert new ClientRange before existing ranges
3080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                range.clients.add(0, new ClientRange(startId, endId, client));
3090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // coalesce range with following ranges up to endIndex
3100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // remove each range after adding its elements, so the index
3110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // of the next range to join is always startIndex+1.
3120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // i is the index if no elements were removed: we only care
3130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // about the number of loop iterations, not the value of i.
3140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                int joinIndex = startIndex + 1;
3150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                for (int i = joinIndex; i <= endIndex; i++) {
3160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    IntRange joinRange = mRanges.get(joinIndex);
3170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    range.clients.addAll(joinRange.clients);
3180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    mRanges.remove(joinRange);
3190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
3200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
3210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
3220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return false;   // failed to update radio
3230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
3240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
32701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [1, 10] existing [2, 3]
3280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // endId extends past all existing IntRanges: combine them all together
32901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(startId, endId, true)) {
3300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.startId = startId;
3310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.endId = endId;
3320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // insert new ClientRange before existing ranges
3330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.clients.add(0, new ClientRange(startId, endId, client));
3340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // coalesce range with following ranges up to len-1
3350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove each range after adding its elements, so the index
3360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // of the next range to join is always startIndex+1.
3370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // i is the index if no elements were removed: we only care
3380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // about the number of loop iterations, not the value of i.
3390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        int joinIndex = startIndex + 1;
3400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        for (int i = joinIndex; i < len; i++) {
34101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // new [1, 10] existing [2, 3] [5, 6]
3420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            IntRange joinRange = mRanges.get(joinIndex);
3430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            range.clients.addAll(joinRange.clients);
3440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            mRanges.remove(joinRange);
3450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
3470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
3480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
3490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
3510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else if ((startId + 1) <= range.endId) {
35201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // new [2, x] existing [1, 4]
3530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (endId <= range.endId) {
35401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 3] existing [1, 4]
3550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // completely contained in existing range; no radio changes
3560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    range.insert(new ClientRange(startId, endId, client));
3570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    return true;
3580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                } else {
35901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 5] existing [1, 4]
3600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // find last range that can coalesce into the new combined range
3610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    int endIndex = startIndex;
3620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    for (int testIndex = startIndex+1; testIndex < len; testIndex++) {
3630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange testRange = mRanges.get(testIndex);
3640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if ((endId + 1) < testRange.startId) {
3650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            break;
3660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
3670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            endIndex = testIndex;
3680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // no adjacent IntRanges to combine
3710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (endIndex == startIndex) {
37201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // new [2, 5] existing [1, 4]
3730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // add range from range.endId+1 to endId,
3740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // values from startId to range.endId are already enabled
37501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (tryAddRanges(range.endId + 1, endId, true)) {
3760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            range.endId = endId;
3770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            range.insert(new ClientRange(startId, endId, client));
3780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return true;
3790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
3800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return false;   // failed to update radio
3810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
3820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
3830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // get last range to coalesce into start range
3840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    IntRange endRange = mRanges.get(endIndex);
3850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // Values from startId to range.endId have already been enabled.
3860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // if endId > endRange.endId, then enable range from range.endId+1 to endId,
3870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // else enable range from range.endId+1 to endRange.startId-1, because
3880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    // values from endRange.startId to endId have already been added.
3890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    int newRangeEndId = (endId <= endRange.endId) ? endRange.startId - 1 : endId;
39001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 10] existing [1, 4] [7, 8] OR
39101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    // new [2, 10] existing [1, 4] [7, 15]
39201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                    if (tryAddRanges(range.endId + 1, newRangeEndId, true)) {
39301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        newRangeEndId = (endId <= endRange.endId) ? endRange.endId : endId;
39401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        range.endId = newRangeEndId;
3950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // insert new ClientRange in place
3960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        range.insert(new ClientRange(startId, endId, client));
39701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // coalesce range with following ranges up to endIndex
3980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove each range after adding its elements, so the index
3990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // of the next range to join is always startIndex+1 (joinIndex).
4000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // i is the index if no elements had been removed: we only care
4010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // about the number of loop iterations, not the value of i.
4020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        int joinIndex = startIndex + 1;
40301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        for (int i = joinIndex; i <= endIndex; i++) {
4040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            IntRange joinRange = mRanges.get(joinIndex);
4050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            range.clients.addAll(joinRange.clients);
4060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            mRanges.remove(joinRange);
4070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
4080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
4090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
4100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // failed to update radio
4110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
4120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
4140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
41601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // new [5, 6], existing [1, 3]
4170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // append new range after existing IntRanges
41801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        if (tryAddRanges(startId, endId, true)) {
4190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            mRanges.add(new IntRange(startId, endId, client));
4200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return true;
4210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
4220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            return false;   // failed to update radio
4230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
4240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
4250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
4270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Disable a range for the specified client and update ranges
4280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * if necessary. If {@link #finishUpdate} returns failure,
4290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * false is returned and the range is not removed.
4300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
4310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
4320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
4330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param client the client requesting to disable the range
4340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
4350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
4360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public synchronized boolean disableRange(int startId, int endId, String client) {
4370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        int len = mRanges.size();
4380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4390825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        for (int i=0; i < len; i++) {
4400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            IntRange range = mRanges.get(i);
4410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (startId < range.startId) {
4420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                return false;   // not found
4430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            } else if (endId <= range.endId) {
4440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // found the IntRange that encloses the client range, if any
4450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // search for it in the clients list
4460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ArrayList<ClientRange> clients = range.clients;
4470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // handle common case of IntRange containing one ClientRange
4490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int crLength = clients.size();
4500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                if (crLength == 1) {
4510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ClientRange cr = clients.get(0);
4520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
45301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // mRange contains only what's enabled.
45401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        // remove the range from mRange then update the radio
45501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        mRanges.remove(i);
45601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (updateRanges()) {
4570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            return true;
4580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        } else {
45901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // failed to update radio.  insert back the range
46001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.add(i, range);
46101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            return false;
4620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
4630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
4640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return false;   // not found
4650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
4660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
4670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
4680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // several ClientRanges: remove one, potentially splitting into many IntRanges.
4690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Save the original start and end id for the original IntRange
4700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // in case the radio update fails and we have to revert it. If the
4710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // update succeeds, we remove the client range and insert the new IntRanges.
47201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // clients are ordered by startId then by endId, so client with largest endId
47301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // can be anywhere.  Need to loop thru to find largestEndId.
4740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                int largestEndId = Integer.MIN_VALUE;  // largest end identifier found
4750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                boolean updateStarted = false;
4760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
47701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                // crlength >= 2
4780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                for (int crIndex=0; crIndex < crLength; crIndex++) {
4790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    ClientRange cr = clients.get(crIndex);
4800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
4810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // found the ClientRange to remove, check if it's the last in the list
4820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (crIndex == crLength - 1) {
4830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            if (range.endId == largestEndId) {
48401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                // remove [2, 5] from [1, 7] [2, 5]
4850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // no channels to remove from radio; return success
4860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                clients.remove(crIndex);
4870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                return true;
4880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
4890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                // disable the channels at the end and lower the end id
49001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                clients.remove(crIndex);
49101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                range.endId = largestEndId;
49201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                if (updateRanges()) {
4930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    return true;
4940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                } else {
49501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    clients.add(crIndex, cr);
49601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    range.endId = cr.endId;
4970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                    return false;
4980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                }
4990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // copy the IntRange so that we can remove elements and modify the
5030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // start and end id's in the copy, leaving the original unmodified
5040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // until after the radio update succeeds
5050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange rangeCopy = new IntRange(range, crIndex);
5060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (crIndex == 0) {
5080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // removing the first ClientRange, so we may need to increase
5090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // the start id of the IntRange.
5100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // We know there are at least two ClientRanges in the list,
51101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // because check for just one ClientRangees case is already handled
5120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // so clients.get(1) should always succeed.
5130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            int nextStartId = clients.get(1).startId;
5140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            if (nextStartId != range.startId) {
5150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                updateStarted = true;
5160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                rangeCopy.startId = nextStartId;
5170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            // init largestEndId
5190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            largestEndId = clients.get(1).endId;
5200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // go through remaining ClientRanges, creating new IntRanges when
5230825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // there is a gap in the sequence. After radio update succeeds,
5240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove the original IntRange and append newRanges to mRanges.
5250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // Otherwise, leave the original IntRange in mRanges and return false.
5260825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        ArrayList<IntRange> newRanges = new ArrayList<IntRange>();
5270825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5280825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        IntRange currentRange = rangeCopy;
5290825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
5300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            ClientRange nextCr = clients.get(nextIndex);
5310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            if (nextCr.startId > largestEndId + 1) {
53201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                updateStarted = true;
5330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                currentRange.endId = largestEndId;
5340825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                newRanges.add(currentRange);
5350825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                currentRange = new IntRange(nextCr);
5360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            } else {
53701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                if (currentRange.endId < nextCr.endId) {
53801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                    currentRange.endId = nextCr.endId;
53901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                                }
5400825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                currentRange.clients.add(nextCr);
5410825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5420825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            if (nextCr.endId > largestEndId) {
5430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                                largestEndId = nextCr.endId;
5440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            }
5450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // remove any channels between largestEndId and endId
5480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (largestEndId < endId) {
54901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            updateStarted = true;
5500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            currentRange.endId = largestEndId;
5510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        newRanges.add(currentRange);
5530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // replace the original IntRange with newRanges
5550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.remove(i);
5560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        mRanges.addAll(i, newRanges);
55701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        if (updateStarted && !updateRanges()) {
55801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            // failed to update radio.  revert back mRange.
55901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.removeAll(newRanges);
56001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            mRanges.add(i, range);
56101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                            return false;
56201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                        }
56301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
5640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        return true;
5650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    } else {
5660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        // not the ClientRange to remove; save highest end ID seen so far
5670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        if (cr.endId > largestEndId) {
5680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                            largestEndId = cr.endId;
5690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        }
5700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    }
5710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                }
5720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
5730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
5740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return false;   // not found
5760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
5790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Perform a complete update operation (enable all ranges). Useful
5800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * after a radio reset. Calls {@link #startUpdate}, followed by zero or
5810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * more calls to {@link #addRange}, followed by {@link #finishUpdate}.
5820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
5830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
5840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean updateRanges() {
5850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        startUpdate();
58601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
58701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        populateAllRanges();
5880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return finishUpdate();
5890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
5900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
5910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
5920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Enable or disable a single range of message identifiers.
5930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
5940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
5950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param selected true to enable range, false to disable range
5960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
5970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
59801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    protected boolean tryAddRanges(int startId, int endId, boolean selected) {
59901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
6000825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        startUpdate();
60101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        populateAllRanges();
60201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // This is the new range to be enabled
60301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        addRange(startId, endId, selected); // adds to mConfigList
6040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return finishUpdate();
6050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns whether the list of ranges is completely empty.
6090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if there are no enabled ranges
6100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public boolean isEmpty() {
6120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return mRanges.isEmpty();
6130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
6140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
61601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Called when attempting to add a single range of message identifiers
61701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Populate all ranges of message identifiers.
61801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     */
61901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    private void populateAllRanges() {
62001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        Iterator<IntRange> itr = mRanges.iterator();
62101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        // Populate all ranges from mRanges
62201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        while (itr.hasNext()) {
62301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            IntRange currRange = (IntRange) itr.next();
62401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            addRange(currRange.startId, currRange.endId, true);
62501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        }
62601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    }
62701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
62801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    /**
62901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Called when attempting to add a single range of message identifiers
63001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     * Populate all ranges of message identifiers using clients' ranges.
63101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks     */
63201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    private void populateAllClientRanges() {
63301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        int len = mRanges.size();
63401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        for (int i = 0; i < len; i++) {
63501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            IntRange range = mRanges.get(i);
63601fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
63701fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            int clientLen = range.clients.size();
63801fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            for (int j=0; j < clientLen; j++) {
63901fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                ClientRange nextRange = range.clients.get(j);
64001fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks                addRange(nextRange.startId, nextRange.endId, true);
64101fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks            }
64201fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks        }
64301fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    }
64401fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks
64501fdbd3285be1a8ba2143a4bc11a0f5065bb68d0Rika Brooks    /**
6460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called when the list of enabled ranges has changed. This will be
6470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * followed by zero or more calls to {@link #addRange} followed by
6480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * a call to {@link #finishUpdate}.
6490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract void startUpdate();
6510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called after {@link #startUpdate} to indicate a range of enabled
6540825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * or disabled values.
6550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
6560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param startId the first id included in the range
6570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param endId the last id included in the range
6580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param selected true to enable range, false to disable range
6590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract void addRange(int startId, int endId, boolean selected);
6610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
6620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
6630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Called to indicate the end of a range update started by the
6640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * previous call to {@link #startUpdate}.
6650825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return true if successful, false otherwise
6660825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
6670825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    protected abstract boolean finishUpdate();
6680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
669