1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.sms;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.ContentValues;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.content.Context;
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.Cursor;
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteDatabase;
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.database.sqlite.SQLiteException;
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.net.Uri;
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.provider.Telephony;
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.mms.ApnSettingsLoader;
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.support.v7.mms.MmsManager;
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.text.TextUtils;
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport android.util.SparseArray;
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.datamodel.data.ParticipantData;
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.mmslib.SqliteWrapper;
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservices;
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.BugleGservicesKeys;
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.LogUtil;
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.OsUtil;
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.android.messaging.util.PhoneUtils;
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.net.URI;
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.net.URISyntaxException;
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.ArrayList;
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport java.util.List;
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * APN loader for default SMS SIM
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * This loader tries to load APNs from 3 sources in order:
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 1. Gservices setting
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 2. System APN table
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * 3. Local APN table
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic class BugleApnSettingsLoader implements ApnSettingsLoader {
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The base implementation of an APN
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static class BaseApn implements Apn {
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Create a base APN from parameters
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param typesIn the APN type field
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param mmscIn the APN mmsc field
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param proxyIn the APN mmsproxy field
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param portIn the APN mmsport field
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @return an instance of base APN, or null if any of the parameter is invalid
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static BaseApn from(final String typesIn, final String mmscIn, final String proxyIn,
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final String portIn) {
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (!isValidApnType(trimWithNullCheck(typesIn), APN_TYPE_MMS)) {
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            String mmsc = trimWithNullCheck(mmscIn);
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (TextUtils.isEmpty(mmsc)) {
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mmsc = trimV4AddrZeros(mmsc);
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            try {
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                new URI(mmsc);
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } catch (final URISyntaxException e) {
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            String mmsProxy = trimWithNullCheck(proxyIn);
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            int mmsProxyPort = 80;
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (!TextUtils.isEmpty(mmsProxy)) {
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mmsProxy = trimV4AddrZeros(mmsProxy);
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final String portString = trimWithNullCheck(portIn);
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (portString != null) {
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    try {
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        mmsProxyPort = Integer.parseInt(portString);
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    } catch (final NumberFormatException e) {
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        // Ignore, just use 80 to try
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    }
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return new BaseApn(mmsc, mmsProxy, mmsProxyPort);
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final String mMmsc;
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final String mMmsProxy;
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final int mMmsProxyPort;
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public BaseApn(final String mmsc, final String proxy, final int port) {
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMmsc = mmsc;
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMmsProxy = proxy;
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mMmsProxyPort = port;
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public String getMmsc() {
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mMmsc;
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public String getMmsProxy() {
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mMmsProxy;
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public int getMmsProxyPort() {
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mMmsProxyPort;
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public void setSuccess() {
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Do nothing
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public boolean equals(final BaseApn other) {
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return TextUtils.equals(mMmsc, other.getMmsc()) &&
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    TextUtils.equals(mMmsProxy, other.getMmsProxy()) &&
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mMmsProxyPort == other.getMmsProxyPort();
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * The APN represented by the local APN table row
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static class DatabaseApn implements Apn {
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final ContentValues CURRENT_NULL_VALUE;
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final ContentValues CURRENT_SET_VALUE;
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        static {
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            CURRENT_NULL_VALUE = new ContentValues(1);
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            CURRENT_NULL_VALUE.putNull(Telephony.Carriers.CURRENT);
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            CURRENT_SET_VALUE = new ContentValues(1);
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            CURRENT_SET_VALUE.put(Telephony.Carriers.CURRENT, "1"); // 1 for auto selected APN
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final String CLEAR_UPDATE_SELECTION = Telephony.Carriers.CURRENT + " =?";
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final String[] CLEAR_UPDATE_SELECTION_ARGS = new String[] { "1" };
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private static final String SET_UPDATE_SELECTION = Telephony.Carriers._ID + " =?";
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Create an APN loaded from local database
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         *
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param apns the in-memory APN list
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param typesIn the APN type field
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param mmscIn the APN mmsc field
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param proxyIn the APN mmsproxy field
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param portIn the APN mmsport field
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param rowId the APN's row ID in database
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @param current the value of CURRENT column in database
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * @return an in-memory APN instance for database APN row, null if parameter invalid
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public static DatabaseApn from(final List<Apn> apns, final String typesIn,
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final String mmscIn, final String proxyIn, final String portIn,
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final long rowId, final int current) {
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (apns == null) {
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final BaseApn base = BaseApn.from(typesIn, mmscIn, proxyIn, portIn);
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (base == null) {
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            for (final ApnSettingsLoader.Apn apn : apns) {
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (apn instanceof DatabaseApn && ((DatabaseApn) apn).equals(base)) {
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return null;
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return new DatabaseApn(apns, base, rowId, current);
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final List<Apn> mApns;
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final BaseApn mBase;
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private final long mRowId;
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private int mCurrent;
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public DatabaseApn(final List<Apn> apns, final BaseApn base, final long rowId,
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final int current) {
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mApns = apns;
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mBase = base;
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mRowId = rowId;
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            mCurrent = current;
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public String getMmsc() {
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mBase.getMmsc();
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public String getMmsProxy() {
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mBase.getMmsProxy();
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public int getMmsProxyPort() {
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mBase.getMmsProxyPort();
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        @Override
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public void setSuccess() {
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            moveToListHead();
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            setCurrentInDatabase();
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Try to move this APN to the head of in-memory list
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private void moveToListHead() {
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // If this is being marked as a successful APN, move it to the top of the list so
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // next time it will be tried first
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            boolean moved = false;
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            synchronized (mApns) {
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (mApns.get(0) != this) {
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mApns.remove(this);
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mApns.add(0, this);
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    moved = true;
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (moved) {
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.d(LogUtil.BUGLE_TAG, "Set APN ["
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + "MMSC=" + getMmsc() + ", "
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + "PROXY=" + getMmsProxy() + ", "
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + "PORT=" + getMmsProxyPort() + "] to be first");
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        /**
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         * Try to set the APN to be CURRENT in its database table
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd         */
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        private void setCurrentInDatabase() {
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            synchronized (this) {
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (mCurrent > 0) {
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // Already current
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return;
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mCurrent = 1;
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.d(LogUtil.BUGLE_TAG, "Set APN @" + mRowId + " to be CURRENT in local db");
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase();
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            database.beginTransaction();
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            try {
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // clear the previous current=1 apn
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // we don't clear current=2 apn since it is manually selected by user
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // and we should not override it.
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                database.update(ApnDatabase.APN_TABLE, CURRENT_NULL_VALUE,
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        CLEAR_UPDATE_SELECTION, CLEAR_UPDATE_SELECTION_ARGS);
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // set this one to be current (1)
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                database.update(ApnDatabase.APN_TABLE, CURRENT_SET_VALUE, SET_UPDATE_SELECTION,
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        new String[] { Long.toString(mRowId) });
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                database.setTransactionSuccessful();
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } finally {
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                database.endTransaction();
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        public boolean equals(final BaseApn other) {
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (other == null) {
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return mBase.equals(other);
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
273d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
274d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * APN_TYPE_ALL is a special type to indicate that this APN entry can
275d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * service all data connections.
276d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
277d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final String APN_TYPE_ALL = "*";
278d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /** APN type for MMS traffic */
279d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static final String APN_TYPE_MMS = "mms";
280d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
281d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String[] APN_PROJECTION_SYSTEM = {
282d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.TYPE,
283d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSC,
284d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSPROXY,
285d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSPORT,
286d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    };
287d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String[] APN_PROJECTION_LOCAL = {
288d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.TYPE,
289d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSC,
290d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSPROXY,
291d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.MMSPORT,
292d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers.CURRENT,
293d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            Telephony.Carriers._ID,
294d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    };
295d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_TYPE         = 0;
296d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_MMSC         = 1;
297d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_MMSPROXY     = 2;
298d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_MMSPORT      = 3;
299d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_CURRENT      = 4;
300d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final int COLUMN_ID           = 5;
301d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
302d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SELECTION_APN = Telephony.Carriers.APN + "=?";
303d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SELECTION_CURRENT = Telephony.Carriers.CURRENT + " IS NOT NULL";
304d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String SELECTION_NUMERIC = Telephony.Carriers.NUMERIC + "=?";
305d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final String ORDER_BY = Telephony.Carriers.CURRENT + " DESC";
306d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
307d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final Context mContext;
308d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
309d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // Cached APNs for subIds
310d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private final SparseArray<List<ApnSettingsLoader.Apn>> mApnsCache;
311d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
312d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public BugleApnSettingsLoader(final Context context) {
313d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mContext = context;
314d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        mApnsCache = new SparseArray<>();
315d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
316d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
317d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
318d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public List<ApnSettingsLoader.Apn> get(final String apnName) {
319d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int subId = PhoneUtils.getDefault().getEffectiveSubId(
320d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                ParticipantData.DEFAULT_SELF_SUB_ID);
321d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        List<ApnSettingsLoader.Apn> apns;
322d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        boolean didLoad = false;
323d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        synchronized (this) {
324d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            apns = mApnsCache.get(subId);
325d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (apns == null) {
326d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                apns = new ArrayList<>();
327d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                mApnsCache.put(subId, apns);
328d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                loadLocked(subId, apnName, apns);
329d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                didLoad = true;
330d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
331d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
332d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (didLoad) {
333d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.i(LogUtil.BUGLE_TAG, "Loaded " + apns.size() + " APNs");
334d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
335d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return apns;
336d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
337d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
338d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void loadLocked(final int subId, final String apnName, final List<Apn> apns) {
339d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Try Gservices first
340d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        loadFromGservices(apns);
341d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (apns.size() > 0) {
342d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
343d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
344d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Try system APN table
345d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        loadFromSystem(subId, apnName, apns);
346d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (apns.size() > 0) {
347d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
348d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
349d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Try local APN table
350d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        loadFromLocalDatabase(apnName, apns);
351d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (apns.size() <= 0) {
352d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Failed to load any APN");
353d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
354d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
355d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
356d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
357d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Load from Gservices if APN setting is set in Gservices
358d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
359d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apns the list used to return results
360d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
361d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void loadFromGservices(final List<Apn> apns) {
362d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final BugleGservices gservices = BugleGservices.get();
363d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String mmsc = gservices.getString(BugleGservicesKeys.MMS_MMSC, null);
364d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(mmsc)) {
365d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
366d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
367d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        LogUtil.i(LogUtil.BUGLE_TAG, "Loading APNs from gservices");
368d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String proxy = gservices.getString(BugleGservicesKeys.MMS_PROXY_ADDRESS, null);
369d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int port = gservices.getInt(BugleGservicesKeys.MMS_PROXY_PORT, -1);
370d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final Apn apn = BaseApn.from("mms", mmsc, proxy, Integer.toString(port));
371d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (apn != null) {
372d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            apns.add(apn);
373d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
374d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
375d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
376d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
377d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Load matching APNs from telephony provider.
378d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * We try different combinations of the query to work around some platform quirks.
379d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
380d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param subId the SIM subId
381d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apnName the APN name to match
382d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apns the list used to return results
383d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
384d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void loadFromSystem(final int subId, final String apnName, final List<Apn> apns) {
385d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Uri uri;
386d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (OsUtil.isAtLeastL_MR1() && subId != MmsManager.DEFAULT_SUB_ID) {
387d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "/subId/" + subId);
388d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
389d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            uri = Telephony.Carriers.CONTENT_URI;
390d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
391d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Cursor cursor = null;
392d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
393d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            for (; ; ) {
394d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // Try different combinations of queries. Some would work on some platforms.
395d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                // So we query each combination until we find one returns non-empty result.
396d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor = querySystem(uri, true/*checkCurrent*/, apnName);
397d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (cursor != null) {
398d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
399d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
400d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor = querySystem(uri, false/*checkCurrent*/, apnName);
401d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (cursor != null) {
402d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
403d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
404d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor = querySystem(uri, true/*checkCurrent*/, null/*apnName*/);
405d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (cursor != null) {
406d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    break;
407d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
408d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor = querySystem(uri, false/*checkCurrent*/, null/*apnName*/);
409d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                break;
410d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
411d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final SecurityException e) {
412d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Can't access platform APN table, return directly
413d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
414d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
415d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (cursor == null) {
416d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
417d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
418d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
419d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor.moveToFirst()) {
420d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final ApnSettingsLoader.Apn apn = BaseApn.from(
421d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_TYPE),
422d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSC),
423d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSPROXY),
424d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSPORT));
425d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (apn != null) {
426d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    apns.add(apn);
427d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
428d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
429d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
430d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor.close();
431d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
432d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
433d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
434d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
435d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Query system APN table
436d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
437d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param uri The APN query URL to use
438d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param checkCurrent If add "CURRENT IS NOT NULL" condition
439d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apnName The optional APN name for query condition
440d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return A cursor of the query result. If a cursor is returned as not null, it is
441d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *         guaranteed to contain at least one row.
442d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
443d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private Cursor querySystem(final Uri uri, final boolean checkCurrent, String apnName) {
444d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        LogUtil.i(LogUtil.BUGLE_TAG, "Loading APNs from system, "
445d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                + "checkCurrent=" + checkCurrent + " apnName=" + apnName);
446d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final StringBuilder selectionBuilder = new StringBuilder();
447d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        String[] selectionArgs = null;
448d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (checkCurrent) {
449d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selectionBuilder.append(SELECTION_CURRENT);
450d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
451d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        apnName = trimWithNullCheck(apnName);
452d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!TextUtils.isEmpty(apnName)) {
453d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (selectionBuilder.length() > 0) {
454d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                selectionBuilder.append(" AND ");
455d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
456d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selectionBuilder.append(SELECTION_APN);
457d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selectionArgs = new String[] { apnName };
458d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
459d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
460d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final Cursor cursor = SqliteWrapper.query(
461d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mContext,
462d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    mContext.getContentResolver(),
463d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    uri,
464d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    APN_PROJECTION_SYSTEM,
465d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    selectionBuilder.toString(),
466d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    selectionArgs,
467d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    null/*sortOrder*/);
468d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor == null || cursor.getCount() < 1) {
469d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (cursor != null) {
470d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    cursor.close();
471d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
472d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                LogUtil.w(LogUtil.BUGLE_TAG, "Query " + uri + " with apn " + apnName + " and "
473d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + (checkCurrent ? "checking CURRENT" : "not checking CURRENT")
474d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        + " returned empty");
475d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return null;
476d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
477d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return cursor;
478d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final SQLiteException e) {
479d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "APN table query exception: " + e);
480d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final SecurityException e) {
481d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Platform restricts APN table access: " + e);
482d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            throw e;
483d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
484d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return null;
485d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
486d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
487d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
488d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Load matching APNs from local APN table.
489d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * We try both using the APN name and not using the APN name.
490d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
491d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apnName the APN name
492d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apns the list of results to return
493d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
494d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private void loadFromLocalDatabase(final String apnName, final List<Apn> apns) {
495d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        LogUtil.i(LogUtil.BUGLE_TAG, "Loading APNs from local APN table");
496d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase();
497d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String mccMnc = PhoneUtils.getMccMncString(PhoneUtils.getDefault().getMccMnc());
498d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Cursor cursor = null;
499d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        cursor = queryLocalDatabase(database, mccMnc, apnName);
500d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (cursor == null) {
501d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor = queryLocalDatabase(database, mccMnc, null/*apnName*/);
502d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
503d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (cursor == null) {
504d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Could not find any APN in local table");
505d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return;
506d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
507d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
508d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            while (cursor.moveToNext()) {
509d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                final Apn apn = DatabaseApn.from(apns,
510d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_TYPE),
511d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSC),
512d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSPROXY),
513d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getString(COLUMN_MMSPORT),
514d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getLong(COLUMN_ID),
515d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                        cursor.getInt(COLUMN_CURRENT));
516d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (apn != null) {
517d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    apns.add(apn);
518d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
519d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
520d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
521d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor.close();
522d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
523d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
524d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
525d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
526d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Make a query of local APN table based on MCC/MNC and APN name, sorted by CURRENT
527d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * column in descending order
528d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
529d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param db the local database
530d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param numeric the MCC/MNC string
531d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param apnName the optional APN name to match
532d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return the cursor of the query, null if no result
533d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
534d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static Cursor queryLocalDatabase(final SQLiteDatabase db, final String numeric,
535d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final String apnName) {
536d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String selection;
537d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String[] selectionArgs;
538d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(apnName)) {
539d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selection = SELECTION_NUMERIC;
540d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selectionArgs = new String[] { numeric };
541d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
542d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selection = SELECTION_NUMERIC + " AND " + SELECTION_APN;
543d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            selectionArgs = new String[] { numeric, apnName };
544d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
545d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Cursor cursor = null;
546d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
547d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor = db.query(ApnDatabase.APN_TABLE, APN_PROJECTION_LOCAL, selection, selectionArgs,
548d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    null/*groupBy*/, null/*having*/, ORDER_BY, null/*limit*/);
549d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final SQLiteException e) {
550d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Local APN table does not exist. Try rebuilding.", e);
551d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            ApnDatabase.forceBuildAndLoadApnTables();
552d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor = db.query(ApnDatabase.APN_TABLE, APN_PROJECTION_LOCAL, selection, selectionArgs,
553d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    null/*groupBy*/, null/*having*/, ORDER_BY, null/*limit*/);
554d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
555d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (cursor == null || cursor.getCount() < 1) {
556d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor != null) {
557d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor.close();
558d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
559d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            LogUtil.w(LogUtil.BUGLE_TAG, "Query local APNs with apn " + apnName
560d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    + " returned empty");
561d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
562d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
563d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return cursor;
564d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
565d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
566d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static String trimWithNullCheck(final String value) {
567d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return value != null ? value.trim() : null;
568d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
569d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
570d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
571d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Trim leading zeros from IPv4 address strings
572d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Our base libraries will interpret that as octel..
573d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Must leave non v4 addresses and host names alone.
574d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * For example, 192.168.000.010 -> 192.168.0.10
575d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
576d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param addr a string representing an ip addr
577d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return a string propertly trimmed
578d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
579d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static String trimV4AddrZeros(final String addr) {
580d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (addr == null) {
581d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return null;
582d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
583d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final String[] octets = addr.split("\\.");
584d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (octets.length != 4) {
585d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return addr;
586d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
587d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final StringBuilder builder = new StringBuilder(16);
588d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        String result = null;
589d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        for (int i = 0; i < 4; i++) {
590d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            try {
591d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (octets[i].length() > 3) {
592d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return addr;
593d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
594d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                builder.append(Integer.parseInt(octets[i]));
595d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } catch (final NumberFormatException e) {
596d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return addr;
597d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
598d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (i < 3) {
599d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                builder.append('.');
600d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
601d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
602d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        result = builder.toString();
603d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return result;
604d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
605d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
606d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
607d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Check if the APN contains the APN type we want
608d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
609d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param types The string encodes a list of supported types
610d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param requestType The type we want
611d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @return true if the input types string contains the requestType
612d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
613d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isValidApnType(final String types, final String requestType) {
614d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // If APN type is unspecified, assume APN_TYPE_ALL.
615d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (TextUtils.isEmpty(types)) {
616d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return true;
617d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
618d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        for (final String t : types.split(",")) {
619d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (t.equals(requestType) || t.equals(APN_TYPE_ALL)) {
620d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return true;
621d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
622d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
623d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return false;
624d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
625d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
626d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
627d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Get the ID of first APN to try
628d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
629d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static String getFirstTryApn(final SQLiteDatabase database, final String mccMnc) {
630d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        String key = null;
631d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        Cursor cursor = null;
632d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        try {
633d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            cursor = queryLocalDatabase(database, mccMnc, null/*apnName*/);
634d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor.moveToFirst()) {
635d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                key = cursor.getString(ApnDatabase.COLUMN_ID);
636d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
637d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } catch (final Exception e) {
638d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // Nothing to do
639d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } finally {
640d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (cursor != null) {
641d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                cursor.close();
642d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
643d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
644d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return key;
645d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
646d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
647