CdmaSubscriptionSourceManager.java revision 58dd6858dc8013b680ea003d22063fd65ed5fe1c
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.cdma;
180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
190825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport java.util.concurrent.atomic.AtomicInteger;
200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
210825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport com.android.internal.telephony.CommandsInterface;
220825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.content.Context;
230825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.AsyncResult;
240825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Handler;
250825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Message;
260825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.Registrant;
270825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.os.RegistrantList;
280825495a331bb44df395a0cdb79fab85e68db5d5Wink Savilleimport android.provider.Settings;
2999c2e1d6749cfad2a8ca94a47857d8c3bfc09454Wink Savilleimport android.telephony.Rlog;
300825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
310825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville/**
320825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville * Class that handles the CDMA subscription source changed events from RIL
330825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville */
340825495a331bb44df395a0cdb79fab85e68db5d5Wink Savillepublic class CdmaSubscriptionSourceManager extends Handler {
35cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville    static final String LOG_TAG = "CdmaSSM";
360825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 1;
370825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int EVENT_GET_CDMA_SUBSCRIPTION_SOURCE     = 2;
380825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final int EVENT_RADIO_ON                         = 3;
39a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int EVENT_SUBSCRIPTION_STATUS_CHANGED      = 4;
40a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville
41a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    // To know subscription is activated
42a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville    private static final int SUBSCRIPTION_ACTIVATED                 = 1;
430825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int SUBSCRIPTION_SOURCE_UNKNOWN = -1;
450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int SUBSCRIPTION_FROM_RUIM      = 0; /* CDMA subscription from RUIM */
460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int SUBSCRIPTION_FROM_NV        = 1; /* CDMA subscription from NV */
470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static final int PREFERRED_CDMA_SUBSCRIPTION = SUBSCRIPTION_FROM_NV;
480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static CdmaSubscriptionSourceManager sInstance;
500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static final Object sReferenceCountMonitor = new Object();
510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private static int sReferenceCount = 0;
520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // ***** Instance Variables
5422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville    private CommandsInterface mCi;
550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private Context mContext;
560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private RegistrantList mCdmaSubscriptionSourceChangedRegistrants = new RegistrantList();
570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // Type of CDMA subscription source
590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private AtomicInteger mCdmaSubscriptionSource = new AtomicInteger(SUBSCRIPTION_FROM_NV);
600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    // Constructor
620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private CdmaSubscriptionSourceManager(Context context, CommandsInterface ci) {
630825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mContext = context;
6422d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mCi = ci;
6522d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mCi.registerForCdmaSubscriptionChanged(this, EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
6622d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville        mCi.registerForOn(this, EVENT_RADIO_ON, null);
675ceae6074e0729fbbc422db2f263bf7cf453bf1aNaveen Kalla        int subscriptionSource = getDefault(context);
687e3260e3a9469b8d5b90665a7f9ccb359f8e708aJunda Liu        log("cdmaSSM constructor: " + subscriptionSource);
690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mCdmaSubscriptionSource.set(subscriptionSource);
70a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville        mCi.registerForSubscriptionStatusChanged(this, EVENT_SUBSCRIPTION_STATUS_CHANGED, null);
710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * This function creates a single instance of this class
750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return object of type CdmaSubscriptionSourceManager
770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public static CdmaSubscriptionSourceManager getInstance(Context context,
790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            CommandsInterface ci, Handler h, int what, Object obj) {
800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (sReferenceCountMonitor) {
810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (null == sInstance) {
820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sInstance = new CdmaSubscriptionSourceManager(context, ci);
830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
84cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville            CdmaSubscriptionSourceManager.sReferenceCount++;
850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        sInstance.registerForCdmaSubscriptionSourceChanged(h, what, obj);
870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return sInstance;
880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Unregisters for the registered event with RIL
920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public void dispose(Handler h) {
940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mCdmaSubscriptionSourceChangedRegistrants.remove(h);
950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        synchronized (sReferenceCountMonitor) {
960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            sReferenceCount--;
970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (sReferenceCount <= 0) {
9822d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mCi.unregisterForCdmaSubscriptionChanged(this);
9922d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mCi.unregisterForOn(this);
100a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                mCi.unregisterForSubscriptionStatusChanged(this);
1010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                sInstance = null;
1020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /*
1070825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * (non-Javadoc)
1080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @see android.os.Handler#handleMessage(android.os.Message)
1090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    @Override
1110825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public void handleMessage(Message msg) {
1120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        AsyncResult ar;
1130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        switch (msg.what) {
1140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
1150825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case EVENT_GET_CDMA_SUBSCRIPTION_SOURCE:
1160825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            {
1170825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                log("CDMA_SUBSCRIPTION_SOURCE event = " + msg.what);
1180825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                ar = (AsyncResult) msg.obj;
1190825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                handleGetCdmaSubscriptionSource(ar);
1200825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1210825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
1220825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            case EVENT_RADIO_ON: {
12322d85a8e3a575a6d01d2c788587971657dfe20c6Wink Saville                mCi.getCdmaSubscriptionSource(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_SOURCE));
1240825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1250825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            break;
126a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            case EVENT_SUBSCRIPTION_STATUS_CHANGED: {
127a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                log("EVENT_SUBSCRIPTION_STATUS_CHANGED");
128a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                ar = (AsyncResult)msg.obj;
129a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                if (ar.exception == null) {
130a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    int actStatus = ((int[])ar.result)[0];
131a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    log("actStatus = " + actStatus);
132a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    if (actStatus == SUBSCRIPTION_ACTIVATED) { // Subscription Activated
133a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                        // In case of multi-SIM, framework should wait for the subscription ready
134a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                        // to send any request to RIL.  Otherwise it will return failure.
135a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                        Rlog.v(LOG_TAG,"get Cdma Subscription Source");
136a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                        mCi.getCdmaSubscriptionSource(
137a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                                obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_SOURCE));
138a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    }
139a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                } else {
140a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                    logw("EVENT_SUBSCRIPTION_STATUS_CHANGED, Exception:" + ar.exception);
141a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville                }
142a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            }
143a8467dd0c524787104b1ccdddc5e8af10ba729edWink Saville            break;
1440825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            default:
1450825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                super.handleMessage(msg);
1460825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
1470825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1480825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1490825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1500825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Returns the current CDMA subscription source value
1510825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return CDMA subscription source value
1520825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1530825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    public int getCdmaSubscriptionSource() {
1547e3260e3a9469b8d5b90665a7f9ccb359f8e708aJunda Liu        log("getcdmasubscriptionSource: " + mCdmaSubscriptionSource.get());
1550825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return mCdmaSubscriptionSource.get();
1560825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1570825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1580825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1590825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Gets the default CDMA subscription source
1600825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
1610825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @return Default CDMA subscription source from Settings DB if present.
1620825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1635ceae6074e0729fbbc422db2f263bf7cf453bf1aNaveen Kalla    public static int getDefault(Context context) {
1640825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        // Get the default value from the Settings
1655ceae6074e0729fbbc422db2f263bf7cf453bf1aNaveen Kalla        int subscriptionSource = Settings.Global.getInt(context.getContentResolver(),
166b8d0e5993929ca9ecd29f406ae5f39a3e450e89bJeff Brown                Settings.Global.CDMA_SUBSCRIPTION_MODE, PREFERRED_CDMA_SUBSCRIPTION);
1677e3260e3a9469b8d5b90665a7f9ccb359f8e708aJunda Liu        Rlog.d(LOG_TAG, "subscriptionSource from settings: " + subscriptionSource);
1680825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        return subscriptionSource;
1690825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1700825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1710825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1720825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Clients automatically register for CDMA subscription source changed event
1730825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * when they get an instance of this object.
1740825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1750825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void registerForCdmaSubscriptionSourceChanged(Handler h, int what, Object obj) {
1760825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        Registrant r = new Registrant (h, what, obj);
1770825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        mCdmaSubscriptionSourceChangedRegistrants.add(r);
1780825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
1790825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1800825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    /**
1810825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * Handles the call to get the subscription source
1820825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *
1830825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     * @param ar AsyncResult object that contains the result of get CDMA
1840825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     *            subscription source call
1850825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville     */
1860825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void handleGetCdmaSubscriptionSource(AsyncResult ar) {
1870825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        if ((ar.exception == null) && (ar.result != null)) {
1880825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            int newSubscriptionSource = ((int[]) ar.result)[0];
1890825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1900825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            if (newSubscriptionSource != mCdmaSubscriptionSource.get()) {
1910825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                log("Subscription Source Changed : " + mCdmaSubscriptionSource + " >> "
1920825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        + newSubscriptionSource);
1930825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mCdmaSubscriptionSource.set(newSubscriptionSource);
1940825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
1950825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                // Notify registrants of the new CDMA subscription source
1960825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                mCdmaSubscriptionSourceChangedRegistrants.notifyRegistrants(new AsyncResult(null,
1970825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                        null, null));
1980825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            }
1990825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        } else {
20058dd6858dc8013b680ea003d22063fd65ed5fe1cAmit Mahajan            // GET_CDMA_SUBSCRIPTION is returning Failure. Probably because modem created GSM Phone.
2010825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville            logw("Unable to get CDMA Subscription Source, Exception: " + ar.exception
2020825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville                    + ", result: " + ar.result);
2030825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville        }
2040825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2050825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2060825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void log(String s) {
207cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        Rlog.d(LOG_TAG, s);
2080825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2090825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2100825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    private void logw(String s) {
211cbaa45bbf2cab852b6c9c3a887e9f803d4e857eaWink Saville        Rlog.w(LOG_TAG, s);
2120825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville    }
2130825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville
2140825495a331bb44df395a0cdb79fab85e68db5d5Wink Saville}
215