FileDocCategorySizeDatePackage
IntRangeManager.javaAPI DocAndroid 5.1 API31904Thu Mar 12 22:22:54 GMT 2015com.android.internal.telephony

IntRangeManager

public abstract class IntRangeManager extends Object
Clients can enable reception of SMS-CB messages for specific ranges of message identifiers (channels). This class keeps track of the currently enabled message identifiers and calls abstract methods to update the radio when the range of enabled message identifiers changes. An update is a call to {@link #startUpdate} followed by zero or more calls to {@link #addRange} followed by a call to {@link #finishUpdate}. Calls to {@link #enableRange} and {@link #disableRange} will perform an incremental update operation if the enabled ranges have changed. A full update operation (i.e. after a radio reset) can be performed by a call to {@link #updateRanges}. Clients are identified by String (the name associated with the User ID of the caller) so that a call to remove a range can be mapped to the client that enabled that range (or else rejected).

Fields Summary
private static final int
INITIAL_CLIENTS_ARRAY_SIZE
Initial capacity for IntRange clients array list. There will be few cell broadcast listeners on a typical device, so this can be small.
private ArrayList
mRanges
List of integer ranges, one per client, sorted by start id.
Constructors Summary
protected IntRangeManager()


      
Methods Summary
protected abstract voidaddRange(int startId, int endId, boolean selected)
Called after {@link #startUpdate} to indicate a range of enabled or disabled values.

param
startId the first id included in the range
param
endId the last id included in the range
param
selected true to enable range, false to disable range

public synchronized booleandisableRange(int startId, int endId, java.lang.String client)
Disable a range for the specified client and update ranges if necessary. If {@link #finishUpdate} returns failure, false is returned and the range is not removed.

param
startId the first id included in the range
param
endId the last id included in the range
param
client the client requesting to disable the range
return
true if successful, false otherwise

        int len = mRanges.size();

        for (int i=0; i < len; i++) {
            IntRange range = mRanges.get(i);
            if (startId < range.mStartId) {
                return false;   // not found
            } else if (endId <= range.mEndId) {
                // found the IntRange that encloses the client range, if any
                // search for it in the clients list
                ArrayList<ClientRange> clients = range.mClients;

                // handle common case of IntRange containing one ClientRange
                int crLength = clients.size();
                if (crLength == 1) {
                    ClientRange cr = clients.get(0);
                    if (cr.mStartId == startId && cr.mEndId == endId && cr.mClient.equals(client)) {
                        // mRange contains only what's enabled.
                        // remove the range from mRange then update the radio
                        mRanges.remove(i);
                        if (updateRanges()) {
                            return true;
                        } else {
                            // failed to update radio.  insert back the range
                            mRanges.add(i, range);
                            return false;
                        }
                    } else {
                        return false;   // not found
                    }
                }

                // several ClientRanges: remove one, potentially splitting into many IntRanges.
                // Save the original start and end id for the original IntRange
                // in case the radio update fails and we have to revert it. If the
                // update succeeds, we remove the client range and insert the new IntRanges.
                // clients are ordered by startId then by endId, so client with largest endId
                // can be anywhere.  Need to loop thru to find largestEndId.
                int largestEndId = Integer.MIN_VALUE;  // largest end identifier found
                boolean updateStarted = false;

                // crlength >= 2
                for (int crIndex=0; crIndex < crLength; crIndex++) {
                    ClientRange cr = clients.get(crIndex);
                    if (cr.mStartId == startId && cr.mEndId == endId && cr.mClient.equals(client)) {
                        // found the ClientRange to remove, check if it's the last in the list
                        if (crIndex == crLength - 1) {
                            if (range.mEndId == largestEndId) {
                                // remove [2, 5] from [1, 7] [2, 5]
                                // no channels to remove from radio; return success
                                clients.remove(crIndex);
                                return true;
                            } else {
                                // disable the channels at the end and lower the end id
                                clients.remove(crIndex);
                                range.mEndId = largestEndId;
                                if (updateRanges()) {
                                    return true;
                                } else {
                                    clients.add(crIndex, cr);
                                    range.mEndId = cr.mEndId;
                                    return false;
                                }
                            }
                        }

                        // copy the IntRange so that we can remove elements and modify the
                        // start and end id's in the copy, leaving the original unmodified
                        // until after the radio update succeeds
                        IntRange rangeCopy = new IntRange(range, crIndex);

                        if (crIndex == 0) {
                            // removing the first ClientRange, so we may need to increase
                            // the start id of the IntRange.
                            // We know there are at least two ClientRanges in the list,
                            // because check for just one ClientRanges case is already handled
                            // so clients.get(1) should always succeed.
                            int nextStartId = clients.get(1).mStartId;
                            if (nextStartId != range.mStartId) {
                                updateStarted = true;
                                rangeCopy.mStartId = nextStartId;
                            }
                            // init largestEndId
                            largestEndId = clients.get(1).mEndId;
                        }

                        // go through remaining ClientRanges, creating new IntRanges when
                        // there is a gap in the sequence. After radio update succeeds,
                        // remove the original IntRange and append newRanges to mRanges.
                        // Otherwise, leave the original IntRange in mRanges and return false.
                        ArrayList<IntRange> newRanges = new ArrayList<IntRange>();

                        IntRange currentRange = rangeCopy;
                        for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
                            ClientRange nextCr = clients.get(nextIndex);
                            if (nextCr.mStartId > largestEndId + 1) {
                                updateStarted = true;
                                currentRange.mEndId = largestEndId;
                                newRanges.add(currentRange);
                                currentRange = new IntRange(nextCr);
                            } else {
                                if (currentRange.mEndId < nextCr.mEndId) {
                                    currentRange.mEndId = nextCr.mEndId;
                                }
                                currentRange.mClients.add(nextCr);
                            }
                            if (nextCr.mEndId > largestEndId) {
                                largestEndId = nextCr.mEndId;
                            }
                        }

                        // remove any channels between largestEndId and endId
                        if (largestEndId < endId) {
                            updateStarted = true;
                            currentRange.mEndId = largestEndId;
                        }
                        newRanges.add(currentRange);

                        // replace the original IntRange with newRanges
                        mRanges.remove(i);
                        mRanges.addAll(i, newRanges);
                        if (updateStarted && !updateRanges()) {
                            // failed to update radio.  revert back mRange.
                            mRanges.removeAll(newRanges);
                            mRanges.add(i, range);
                            return false;
                        }

                        return true;
                    } else {
                        // not the ClientRange to remove; save highest end ID seen so far
                        if (cr.mEndId > largestEndId) {
                            largestEndId = cr.mEndId;
                        }
                    }
                }
            }
        }

        return false;   // not found
    
public synchronized booleanenableRange(int startId, int endId, java.lang.String client)
Enable a range for the specified client and update ranges if necessary. If {@link #finishUpdate} returns failure, false is returned and the range is not added.

param
startId the first id included in the range
param
endId the last id included in the range
param
client the client requesting the enabled range
return
true if successful, false otherwise

        int len = mRanges.size();

        // empty range list: add the initial IntRange
        if (len == 0) {
            if (tryAddRanges(startId, endId, true)) {
                mRanges.add(new IntRange(startId, endId, client));
                return true;
            } else {
                return false;   // failed to update radio
            }
        }

        for (int startIndex = 0; startIndex < len; startIndex++) {
            IntRange range = mRanges.get(startIndex);
            if ((startId) >= range.mStartId && (endId) <= range.mEndId) {
                // exact same range:  new [1, 1] existing [1, 1]
                // range already enclosed in existing: new [3, 3], [1,3]
                // no radio update necessary.
                // duplicate "client" check is done in insert, attempt to insert.
                range.insert(new ClientRange(startId, endId, client));
                return true;
            } else if ((startId - 1) == range.mEndId) {
                // new [3, x] existing [1, 2]  OR new [2, 2] existing [1, 1]
                // found missing link? check if next range can be joined
                int newRangeEndId = endId;
                IntRange nextRange = null;
                if ((startIndex + 1) < len) {
                    nextRange = mRanges.get(startIndex + 1);
                    if ((nextRange.mStartId - 1) <= endId) {
                        // new [3, x] existing [1, 2] [5, 7] OR  new [2 , 2] existing [1, 1] [3, 5]
                        if (endId <= nextRange.mEndId) {
                            // new [3, 6] existing [1, 2] [5, 7]
                            newRangeEndId = nextRange.mStartId - 1; // need to enable [3, 4]
                        }
                    } else {
                        // mark nextRange to be joined as null.
                        nextRange = null;
                    }
                }
                if (tryAddRanges(startId, newRangeEndId, true)) {
                    range.mEndId = endId;
                    range.insert(new ClientRange(startId, endId, client));

                    // found missing link? check if next range can be joined
                    if (nextRange != null) {
                        if (range.mEndId < nextRange.mEndId) {
                            // new [3, 6] existing [1, 2] [5, 10]
                            range.mEndId = nextRange.mEndId;
                        }
                        range.mClients.addAll(nextRange.mClients);
                        mRanges.remove(nextRange);
                    }
                    return true;
                } else {
                    return false;   // failed to update radio
                }
            } else if (startId < range.mStartId) {
                // new [1, x] , existing [5, y]
                // test if new range completely precedes this range
                // note that [1, 4] and [5, 6] coalesce to [1, 6]
                if ((endId + 1) < range.mStartId) {
                    // new [1, 3] existing [5, 6] non contiguous case
                    // insert new int range before previous first range
                    if (tryAddRanges(startId, endId, true)) {
                        mRanges.add(startIndex, new IntRange(startId, endId, client));
                        return true;
                    } else {
                        return false;   // failed to update radio
                    }
                } else if (endId <= range.mEndId) {
                    // new [1, 4] existing [5, 6]  or  new [1, 1] existing [2, 2]
                    // extend the start of this range
                    if (tryAddRanges(startId, range.mStartId - 1, true)) {
                        range.mStartId = startId;
                        range.mClients.add(0, new ClientRange(startId, endId, client));
                        return true;
                    } else {
                        return false;   // failed to update radio
                    }
                } else {
                    // find last range that can coalesce into the new combined range
                    for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
                        IntRange endRange = mRanges.get(endIndex);
                        if ((endId + 1) < endRange.mStartId) {
                            // new [1, 10] existing [2, 3] [14, 15]
                            // try to add entire new range
                            if (tryAddRanges(startId, endId, true)) {
                                range.mStartId = startId;
                                range.mEndId = endId;
                                // insert new ClientRange before existing ranges
                                range.mClients.add(0, new ClientRange(startId, endId, client));
                                // coalesce range with following ranges up to endIndex-1
                                // remove each range after adding its elements, so the index
                                // of the next range to join is always startIndex+1.
                                // i is the index if no elements were removed: we only care
                                // about the number of loop iterations, not the value of i.
                                int joinIndex = startIndex + 1;
                                for (int i = joinIndex; i < endIndex; i++) {
                                    // new [1, 10] existing [2, 3] [5, 6] [14, 15]
                                    IntRange joinRange = mRanges.get(joinIndex);
                                    range.mClients.addAll(joinRange.mClients);
                                    mRanges.remove(joinRange);
                                }
                                return true;
                            } else {
                                return false;   // failed to update radio
                            }
                        } else if (endId <= endRange.mEndId) {
                            // new [1, 10] existing [2, 3] [5, 15]
                            // add range from start id to start of last overlapping range,
                            // values from endRange.startId to endId are already enabled
                            if (tryAddRanges(startId, endRange.mStartId - 1, true)) {
                                range.mStartId = startId;
                                range.mEndId = endRange.mEndId;
                                // insert new ClientRange before existing ranges
                                range.mClients.add(0, new ClientRange(startId, endId, client));
                                // coalesce range with following ranges up to endIndex
                                // remove each range after adding its elements, so the index
                                // of the next range to join is always startIndex+1.
                                // i is the index if no elements were removed: we only care
                                // about the number of loop iterations, not the value of i.
                                int joinIndex = startIndex + 1;
                                for (int i = joinIndex; i <= endIndex; i++) {
                                    IntRange joinRange = mRanges.get(joinIndex);
                                    range.mClients.addAll(joinRange.mClients);
                                    mRanges.remove(joinRange);
                                }
                                return true;
                            } else {
                                return false;   // failed to update radio
                            }
                        }
                    }

                    // new [1, 10] existing [2, 3]
                    // endId extends past all existing IntRanges: combine them all together
                    if (tryAddRanges(startId, endId, true)) {
                        range.mStartId = startId;
                        range.mEndId = endId;
                        // insert new ClientRange before existing ranges
                        range.mClients.add(0, new ClientRange(startId, endId, client));
                        // coalesce range with following ranges up to len-1
                        // remove each range after adding its elements, so the index
                        // of the next range to join is always startIndex+1.
                        // i is the index if no elements were removed: we only care
                        // about the number of loop iterations, not the value of i.
                        int joinIndex = startIndex + 1;
                        for (int i = joinIndex; i < len; i++) {
                            // new [1, 10] existing [2, 3] [5, 6]
                            IntRange joinRange = mRanges.get(joinIndex);
                            range.mClients.addAll(joinRange.mClients);
                            mRanges.remove(joinRange);
                        }
                        return true;
                    } else {
                        return false;   // failed to update radio
                    }
                }
            } else if ((startId + 1) <= range.mEndId) {
                // new [2, x] existing [1, 4]
                if (endId <= range.mEndId) {
                    // new [2, 3] existing [1, 4]
                    // completely contained in existing range; no radio changes
                    range.insert(new ClientRange(startId, endId, client));
                    return true;
                } else {
                    // new [2, 5] existing [1, 4]
                    // find last range that can coalesce into the new combined range
                    int endIndex = startIndex;
                    for (int testIndex = startIndex+1; testIndex < len; testIndex++) {
                        IntRange testRange = mRanges.get(testIndex);
                        if ((endId + 1) < testRange.mStartId) {
                            break;
                        } else {
                            endIndex = testIndex;
                        }
                    }
                    // no adjacent IntRanges to combine
                    if (endIndex == startIndex) {
                        // new [2, 5] existing [1, 4]
                        // add range from range.endId+1 to endId,
                        // values from startId to range.endId are already enabled
                        if (tryAddRanges(range.mEndId + 1, endId, true)) {
                            range.mEndId = endId;
                            range.insert(new ClientRange(startId, endId, client));
                            return true;
                        } else {
                            return false;   // failed to update radio
                        }
                    }
                    // get last range to coalesce into start range
                    IntRange endRange = mRanges.get(endIndex);
                    // Values from startId to range.endId have already been enabled.
                    // if endId > endRange.endId, then enable range from range.endId+1 to endId,
                    // else enable range from range.endId+1 to endRange.startId-1, because
                    // values from endRange.startId to endId have already been added.
                    int newRangeEndId = (endId <= endRange.mEndId) ? endRange.mStartId - 1 : endId;
                    // new [2, 10] existing [1, 4] [7, 8] OR
                    // new [2, 10] existing [1, 4] [7, 15]
                    if (tryAddRanges(range.mEndId + 1, newRangeEndId, true)) {
                        newRangeEndId = (endId <= endRange.mEndId) ? endRange.mEndId : endId;
                        range.mEndId = newRangeEndId;
                        // insert new ClientRange in place
                        range.insert(new ClientRange(startId, endId, client));
                        // coalesce range with following ranges up to endIndex
                        // remove each range after adding its elements, so the index
                        // of the next range to join is always startIndex+1 (joinIndex).
                        // i is the index if no elements had been removed: we only care
                        // about the number of loop iterations, not the value of i.
                        int joinIndex = startIndex + 1;
                        for (int i = joinIndex; i <= endIndex; i++) {
                            IntRange joinRange = mRanges.get(joinIndex);
                            range.mClients.addAll(joinRange.mClients);
                            mRanges.remove(joinRange);
                        }
                        return true;
                    } else {
                        return false;   // failed to update radio
                    }
                }
            }
        }

        // new [5, 6], existing [1, 3]
        // append new range after existing IntRanges
        if (tryAddRanges(startId, endId, true)) {
            mRanges.add(new IntRange(startId, endId, client));
            return true;
        } else {
            return false;   // failed to update radio
        }
    
protected abstract booleanfinishUpdate()
Called to indicate the end of a range update started by the previous call to {@link #startUpdate}.

return
true if successful, false otherwise

public booleanisEmpty()
Returns whether the list of ranges is completely empty.

return
true if there are no enabled ranges

        return mRanges.isEmpty();
    
private voidpopulateAllClientRanges()
Called when attempting to add a single range of message identifiers Populate all ranges of message identifiers using clients' ranges.

        int len = mRanges.size();
        for (int i = 0; i < len; i++) {
            IntRange range = mRanges.get(i);

            int clientLen = range.mClients.size();
            for (int j=0; j < clientLen; j++) {
                ClientRange nextRange = range.mClients.get(j);
                addRange(nextRange.mStartId, nextRange.mEndId, true);
            }
        }
    
private voidpopulateAllRanges()
Called when attempting to add a single range of message identifiers Populate all ranges of message identifiers.

        Iterator<IntRange> itr = mRanges.iterator();
        // Populate all ranges from mRanges
        while (itr.hasNext()) {
            IntRange currRange = (IntRange) itr.next();
            addRange(currRange.mStartId, currRange.mEndId, true);
        }
    
protected abstract voidstartUpdate()
Called when the list of enabled ranges has changed. This will be followed by zero or more calls to {@link #addRange} followed by a call to {@link #finishUpdate}.

protected booleantryAddRanges(int startId, int endId, boolean selected)
Enable or disable a single range of message identifiers.

param
startId the first id included in the range
param
endId the last id included in the range
param
selected true to enable range, false to disable range
return
true if successful, false otherwise


        startUpdate();
        populateAllRanges();
        // This is the new range to be enabled
        addRange(startId, endId, selected); // adds to mConfigList
        return finishUpdate();
    
public booleanupdateRanges()
Perform a complete update operation (enable all ranges). Useful after a radio reset. Calls {@link #startUpdate}, followed by zero or more calls to {@link #addRange}, followed by {@link #finishUpdate}.

return
true if successful, false otherwise

        startUpdate();

        populateAllRanges();
        return finishUpdate();