blob: 66cce283384fb9de3d47a12c319e6d234a71cbb6 [file] [log] [blame]
Jonathan Basseri6465afd2015-02-25 13:05:57 -08001/**
2 * Copyright (c) 2015, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Jonathan Basseri6465afd2015-02-25 13:05:57 -080017package com.android.phone;
18
19import static android.Manifest.permission.READ_PHONE_STATE;
20import static com.android.internal.telephony.uicc.IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED;
21
Jonathan Basseri11f89572015-05-05 11:57:39 -070022import android.annotation.NonNull;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080023import android.app.ActivityManagerNative;
24import android.content.BroadcastReceiver;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.ServiceConnection;
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070030import android.content.pm.PackageInfo;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070031import android.content.pm.PackageManager;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080032import android.database.sqlite.SQLiteDatabase;
33import android.database.sqlite.SQLiteOpenHelper;
34import android.os.AsyncResult;
Junda Liu43d723a2015-05-12 17:23:45 -070035import android.os.Binder;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080036import android.os.Handler;
37import android.os.IBinder;
38import android.os.Message;
Jonathan Basseric31f1f32015-05-12 10:13:03 -070039import android.os.PersistableBundle;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080040import android.os.RemoteException;
41import android.os.ServiceManager;
42import android.os.UserHandle;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080043import android.service.carrier.CarrierIdentifier;
Zach Johnson36d7aab2015-05-22 15:43:00 -070044import android.service.carrier.CarrierService;
45import android.service.carrier.ICarrierService;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080046import android.telephony.CarrierConfigManager;
47import android.telephony.SubscriptionManager;
48import android.telephony.TelephonyManager;
49import android.util.Log;
50
51import com.android.internal.telephony.ICarrierConfigLoader;
52import com.android.internal.telephony.IccCardConstants;
53import com.android.internal.telephony.Phone;
54import com.android.internal.telephony.PhoneConstants;
55import com.android.internal.telephony.PhoneFactory;
56import com.android.internal.telephony.TelephonyIntents;
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070057import com.android.internal.util.FastXmlSerializer;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080058
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070059import org.xmlpull.v1.XmlPullParser;
60import org.xmlpull.v1.XmlPullParserException;
61import org.xmlpull.v1.XmlPullParserFactory;
62
63import java.io.File;
Junda Liu43d723a2015-05-12 17:23:45 -070064import java.io.FileDescriptor;
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070065import java.io.FileInputStream;
66import java.io.FileNotFoundException;
67import java.io.FileOutputStream;
Jonathan Basseri1f743c92015-05-15 00:19:46 -070068import java.io.FilenameFilter;
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070069import java.io.IOException;
Junda Liu43d723a2015-05-12 17:23:45 -070070import java.io.PrintWriter;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080071import java.util.List;
72
73/**
74 * CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
Jonathan Basseri6465afd2015-02-25 13:05:57 -080075 * TODO: handle package install/uninstall events
76 */
77
78public class CarrierConfigLoader extends ICarrierConfigLoader.Stub {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -070079 private static final String LOG_TAG = "CarrierConfigLoader";
Jonathan Basseri6465afd2015-02-25 13:05:57 -080080 // Package name for default carrier config app, bundled with system image.
81 private static final String DEFAULT_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig";
82
83 /** The singleton instance. */
84 private static CarrierConfigLoader sInstance;
85 // The context for phone app, passed from PhoneGlobals.
86 private Context mContext;
87 // Carrier configs from default app, indexed by phoneID.
Jonathan Basseric31f1f32015-05-12 10:13:03 -070088 private PersistableBundle[] mConfigFromDefaultApp;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080089 // Carrier configs from privileged carrier config app, indexed by phoneID.
Jonathan Basseric31f1f32015-05-12 10:13:03 -070090 private PersistableBundle[] mConfigFromCarrierApp;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080091 // Service connection for binding to config app.
Zach Johnson36d7aab2015-05-22 15:43:00 -070092 private CarrierServiceConnection[] mServiceConnection;
Jonathan Basseri6465afd2015-02-25 13:05:57 -080093
94 // Broadcast receiver for SIM and pkg intents, register intent filter in constructor.
95 private final BroadcastReceiver mReceiver = new ConfigLoaderBroadcastReceiver();
96
97 // Message codes; see mHandler below.
98 // Request from SubscriptionInfoUpdater when SIM becomes absent or error.
99 private static final int EVENT_CLEAR_CONFIG = 0;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800100 // Has connected to default app.
101 private static final int EVENT_CONNECTED_TO_DEFAULT = 3;
102 // Has connected to carrier app.
103 private static final int EVENT_CONNECTED_TO_CARRIER = 4;
104 // Config has been loaded from default app.
105 private static final int EVENT_LOADED_FROM_DEFAULT = 5;
106 // Config has been loaded from carrier app.
107 private static final int EVENT_LOADED_FROM_CARRIER = 6;
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700108 // Attempt to fetch from default app or read from XML.
109 private static final int EVENT_FETCH_DEFAULT = 7;
110 // Attempt to fetch from carrier app or read from XML.
111 private static final int EVENT_FETCH_CARRIER = 8;
112 // A package has been installed, uninstalled, or updated.
113 private static final int EVENT_PACKAGE_CHANGED = 9;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800114
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700115 // Tags used for saving and restoring XML documents.
116 private static final String TAG_DOCUMENT = "carrier_config";
117 private static final String TAG_VERSION = "package_version";
118 private static final String TAG_BUNDLE = "bundle_data";
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800119
120 // Handler to process various events.
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700121 //
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700122 // For each phoneId, the event sequence should be:
123 // fetch default, connected to default, loaded from default,
124 // fetch carrier, connected to carrier, loaded from carrier.
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700125 //
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700126 // If there is a saved config file for either the default app or the carrier app, we skip
127 // binding to the app and go straight from fetch to loaded.
128 //
129 // At any time, at most one connection is active. If events are not in this order, previous
130 // connection will be unbound, so only latest event takes effect.
131 //
132 // We broadcast ACTION_CARRIER_CONFIG_CHANGED after:
133 // 1. loading from carrier app (even if read from a file)
134 // 2. loading from default app if there is no carrier app (even if read from a file)
135 // 3. clearing config (e.g. due to sim removal)
136 // 4. encountering bind or IPC error
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800137 private Handler mHandler = new Handler() {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700138 @Override
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800139 public void handleMessage(Message msg) {
140 int phoneId = msg.arg1;
141 log("mHandler: " + msg.what + " phoneId: " + phoneId);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700142 String iccid;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800143 CarrierIdentifier carrierId;
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700144 String carrierPackageName;
Zach Johnson36d7aab2015-05-22 15:43:00 -0700145 CarrierServiceConnection conn;
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700146 PersistableBundle config;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800147 switch (msg.what) {
148 case EVENT_CLEAR_CONFIG:
Junda Liu0486bdb2015-05-22 15:01:35 -0700149 if (mConfigFromDefaultApp[phoneId] == null &&
150 mConfigFromCarrierApp[phoneId] == null)
151 break;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800152 mConfigFromDefaultApp[phoneId] = null;
153 mConfigFromCarrierApp[phoneId] = null;
154 mServiceConnection[phoneId] = null;
155 broadcastConfigChangedIntent(phoneId);
156 break;
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700157
158 case EVENT_PACKAGE_CHANGED:
159 carrierPackageName = (String) msg.obj;
160 deleteConfigForPackage(carrierPackageName);
161 int numPhones = TelephonyManager.from(mContext).getPhoneCount();
162 for (int i = 0; i < numPhones; ++i) {
163 updateConfigForPhoneId(i);
164 }
165 break;
166
167 case EVENT_FETCH_DEFAULT:
168 iccid = getIccIdForPhoneId(phoneId);
169 config = restoreConfigFromXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid);
170 if (config != null) {
171 log("Loaded config from XML. package=" + DEFAULT_CARRIER_CONFIG_PACKAGE
172 + " phoneId=" + phoneId);
173 mConfigFromDefaultApp[phoneId] = config;
174 Message newMsg = obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1);
175 newMsg.getData().putBoolean("loaded_from_xml", true);
176 mHandler.sendMessage(newMsg);
177 } else {
178 if (!bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE,
179 phoneId, EVENT_CONNECTED_TO_DEFAULT)) {
180 // Send bcast if bind fails
181 broadcastConfigChangedIntent(phoneId);
182 }
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800183 }
184 break;
185
186 case EVENT_CONNECTED_TO_DEFAULT:
187 carrierId = getCarrierIdForPhoneId(phoneId);
Zach Johnson36d7aab2015-05-22 15:43:00 -0700188 conn = (CarrierServiceConnection) msg.obj;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800189 // If new service connection has been created, unbind.
190 if (mServiceConnection[phoneId] != conn || conn.service == null) {
191 mContext.unbindService(conn);
192 break;
193 }
194 try {
Zach Johnson36d7aab2015-05-22 15:43:00 -0700195 ICarrierService carrierService = ICarrierService.Stub
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700196 .asInterface(conn.service);
Zach Johnson36d7aab2015-05-22 15:43:00 -0700197 config = carrierService.getCarrierConfig(carrierId);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700198 iccid = getIccIdForPhoneId(phoneId);
199 saveConfigToXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid, config);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800200 mConfigFromDefaultApp[phoneId] = config;
Junda Liu9f2d2712015-05-15 14:22:45 -0700201 sendMessage(obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1));
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800202 } catch (RemoteException ex) {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700203 loge("Failed to get carrier config: " + ex.toString());
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800204 } finally {
205 mContext.unbindService(mServiceConnection[phoneId]);
206 }
207 break;
208
209 case EVENT_LOADED_FROM_DEFAULT:
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700210 // If we attempted to bind to the app, but the service connection is null, then
211 // config was cleared while we were waiting and we should not continue.
212 if (!msg.getData().getBoolean("loaded_from_xml", false)
213 && mServiceConnection[phoneId] == null) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800214 break;
215 }
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700216 carrierPackageName = getCarrierPackageForPhoneId(phoneId);
217 if (carrierPackageName != null) {
218 log("Found carrier config app: " + carrierPackageName);
219 sendMessage(obtainMessage(EVENT_FETCH_CARRIER, phoneId));
Jonathan Basserib919e932015-05-27 16:53:08 +0000220 } else {
221 broadcastConfigChangedIntent(phoneId);
Jonathan Basseri4ae2e7c2015-05-15 00:19:46 -0700222 }
223 break;
224
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700225 case EVENT_FETCH_CARRIER:
226 carrierPackageName = getCarrierPackageForPhoneId(phoneId);
227 iccid = getIccIdForPhoneId(phoneId);
228 config = restoreConfigFromXml(carrierPackageName, iccid);
229 if (config != null) {
230 log("Loaded config from XML. package=" + carrierPackageName + " phoneId="
231 + phoneId);
232 mConfigFromCarrierApp[phoneId] = config;
233 Message newMsg = obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1);
234 newMsg.getData().putBoolean("loaded_from_xml", true);
235 sendMessage(newMsg);
236 } else {
237 if (!bindToConfigPackage(carrierPackageName, phoneId,
238 EVENT_CONNECTED_TO_CARRIER)) {
239 // Send bcast if bind fails
240 broadcastConfigChangedIntent(phoneId);
241 }
242 }
243 break;
244
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800245 case EVENT_CONNECTED_TO_CARRIER:
246 carrierId = getCarrierIdForPhoneId(phoneId);
Zach Johnson36d7aab2015-05-22 15:43:00 -0700247 conn = (CarrierServiceConnection) msg.obj;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800248 // If new service connection has been created, unbind.
249 if (mServiceConnection[phoneId] != conn ||
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700250 conn.service == null) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800251 mContext.unbindService(conn);
252 break;
253 }
254 try {
Zach Johnson36d7aab2015-05-22 15:43:00 -0700255 ICarrierService carrierService = ICarrierService.Stub
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700256 .asInterface(conn.service);
Zach Johnson36d7aab2015-05-22 15:43:00 -0700257 config = carrierService.getCarrierConfig(carrierId);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700258 carrierPackageName = getCarrierPackageForPhoneId(phoneId);
259 iccid = getIccIdForPhoneId(phoneId);
260 saveConfigToXml(carrierPackageName, iccid, config);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800261 mConfigFromCarrierApp[phoneId] = config;
Junda Liu9f2d2712015-05-15 14:22:45 -0700262 sendMessage(obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1));
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800263 } catch (RemoteException ex) {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700264 loge("Failed to get carrier config: " + ex.toString());
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800265 } finally {
266 mContext.unbindService(mServiceConnection[phoneId]);
267 }
268 break;
269
270 case EVENT_LOADED_FROM_CARRIER:
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700271 // If we attempted to bind to the app, but the service connection is null, then
272 // config was cleared while we were waiting and we should not continue.
273 if (!msg.getData().getBoolean("loaded_from_xml", false)
274 && mServiceConnection[phoneId] == null) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800275 break;
276 }
277 broadcastConfigChangedIntent(phoneId);
278 break;
279 }
280 }
281 };
282
283 /**
284 * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast
285 * receiver for relevant events.
286 */
287 private CarrierConfigLoader(Context context) {
288 mContext = context;
289
290 // Register for package updates.
291 IntentFilter triggers = new IntentFilter();
292 triggers.addAction(Intent.ACTION_PACKAGE_ADDED);
293 triggers.addAction(Intent.ACTION_PACKAGE_CHANGED);
294 triggers.addAction(Intent.ACTION_PACKAGE_REMOVED);
295 mContext.registerReceiver(mReceiver, triggers);
296
297 int numPhones = TelephonyManager.from(context).getPhoneCount();
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700298 mConfigFromDefaultApp = new PersistableBundle[numPhones];
299 mConfigFromCarrierApp = new PersistableBundle[numPhones];
Zach Johnson36d7aab2015-05-22 15:43:00 -0700300 mServiceConnection = new CarrierServiceConnection[numPhones];
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800301 // Make this service available through ServiceManager.
302 ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this);
303 log("CarrierConfigLoader has started");
304 }
305
306 /**
307 * Initialize the singleton CarrierConfigLoader instance.
308 *
309 * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}.
310 */
311 /* package */
312 static CarrierConfigLoader init(Context context) {
313 synchronized (CarrierConfigLoader.class) {
314 if (sInstance == null) {
315 sInstance = new CarrierConfigLoader(context);
316 } else {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700317 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800318 }
319 return sInstance;
320 }
321 }
322
323 private void broadcastConfigChangedIntent(int phoneId) {
324 Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
325 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
326 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId);
327 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700328 UserHandle.USER_ALL);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800329 }
330
331 /** Binds to the default or carrier config app. */
332 private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) {
333 log("Binding to " + pkgName + " for phone " + phoneId);
Zach Johnsona9d689f2015-05-27 16:23:22 -0700334 Intent carrierService = new Intent(CarrierService.CONFIG_SERVICE_INTERFACE);
Zach Johnson36d7aab2015-05-22 15:43:00 -0700335 carrierService.setPackage(pkgName);
336 mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800337 try {
Zach Johnson36d7aab2015-05-22 15:43:00 -0700338 return mContext.bindService(carrierService, mServiceConnection[phoneId],
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800339 Context.BIND_AUTO_CREATE);
340 } catch (SecurityException ex) {
341 return false;
342 }
343 }
344
345 private CarrierIdentifier getCarrierIdForPhoneId(int phoneId) {
346 String mcc = "";
347 String mnc = "";
348 String imsi = "";
349 String gid1 = "";
350 String gid2 = "";
351 String spn = TelephonyManager.from(mContext).getSimOperatorNameForPhone(phoneId);
352 String simOperator = TelephonyManager.from(mContext).getSimOperatorNumericForPhone(phoneId);
Jonathan Basseri1fa437c2015-04-20 11:08:18 -0700353 // A valid simOperator should be 5 or 6 digits, depending on the length of the MNC.
354 if (simOperator != null && simOperator.length() >= 3) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800355 mcc = simOperator.substring(0, 3);
356 mnc = simOperator.substring(3);
357 }
358 Phone phone = PhoneFactory.getPhone(phoneId);
359 if (phone != null) {
360 imsi = phone.getSubscriberId();
361 gid1 = phone.getGroupIdLevel1();
Junda Liu73183532015-05-14 13:55:40 -0700362 gid2 = phone.getGroupIdLevel2();
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800363 }
364
365 return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2);
366 }
367
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700368 /** Returns the package name of a priveleged carrier app, or null if there is none. */
369 private String getCarrierPackageForPhoneId(int phoneId) {
370 List<String> carrierPackageNames = TelephonyManager.from(mContext)
371 .getCarrierPackageNamesForIntentAndPhone(
372 new Intent(CarrierService.CONFIG_SERVICE_INTERFACE), phoneId);
373 if (carrierPackageNames != null && carrierPackageNames.size() > 0) {
374 return carrierPackageNames.get(0);
375 } else {
376 return null;
377 }
378 }
379
380 private String getIccIdForPhoneId(int phoneId) {
381 if (!SubscriptionManager.isValidPhoneId(phoneId)) {
382 return null;
383 }
384 Phone phone = PhoneFactory.getPhone(phoneId);
385 if (phone == null) {
386 return null;
387 }
388 return phone.getIccSerialNumber();
389 }
390
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700391 /**
392 * Writes a bundle to an XML file.
393 *
394 * The bundle will be written to a file named after the package name and ICCID, so that it can
395 * be restored later with {@link @restoreConfigFromXml}. The XML output will include the bundle
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700396 * and the current version of the specified package.
397 *
398 * In case of errors or invalid input, no file will be written.
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700399 *
400 * @param packageName the name of the package from which we fetched this bundle.
401 * @param iccid the ICCID of the subscription for which this bundle was fetched.
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700402 * @param config the bundle to be written. Null will be treated as an empty bundle.
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700403 */
404 private void saveConfigToXml(String packageName, String iccid, PersistableBundle config) {
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700405 if (packageName == null || iccid == null) {
406 loge("Cannot save config with null packageName or iccid.");
407 return;
408 }
409 if (config == null) {
410 config = new PersistableBundle();
411 }
412
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700413 final String version = getPackageVersion(packageName);
414 if (version == null) {
415 loge("Failed to get package version for: " + packageName);
416 return;
417 }
418
419 FileOutputStream outFile = null;
420 try {
421 outFile = new FileOutputStream(
422 new File(mContext.getFilesDir(), getFilenameForConfig(packageName, iccid)));
423 FastXmlSerializer out = new FastXmlSerializer();
424 out.setOutput(outFile, "utf-8");
425 out.startDocument("utf-8", true);
426 out.startTag(null, TAG_DOCUMENT);
427 out.startTag(null, TAG_VERSION);
428 out.text(version);
429 out.endTag(null, TAG_VERSION);
430 out.startTag(null, TAG_BUNDLE);
431 config.saveToXml(out);
432 out.endTag(null, TAG_BUNDLE);
433 out.endTag(null, TAG_DOCUMENT);
434 out.endDocument();
435 out.flush();
436 outFile.close();
437 }
438 catch (IOException e) {
439 loge(e.toString());
440 }
441 catch (XmlPullParserException e) {
442 loge(e.toString());
443 }
444 }
445
446 /**
447 * Reads a bundle from an XML file.
448 *
449 * This restores a bundle that was written with {@link #saveConfigToXml}. This returns the saved
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700450 * config bundle for the given package and ICCID.
451 *
452 * In case of errors, or if the saved config is from a different package version than the
453 * current version, then null will be returned.
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700454 *
455 * @param packageName the name of the package from which we fetched this bundle.
456 * @param iccid the ICCID of the subscription for which this bundle was fetched.
457 * @return the bundle from the XML file. Returns null if there is no saved config, the saved
458 * version does not match, or reading config fails.
459 */
460 private PersistableBundle restoreConfigFromXml(String packageName, String iccid) {
461 final String version = getPackageVersion(packageName);
462 if (version == null) {
463 loge("Failed to get package version for: " + packageName);
464 return null;
465 }
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700466 if (packageName == null || iccid == null) {
467 loge("Cannot restore config with null packageName or iccid.");
468 return null;
469 }
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700470
471 PersistableBundle restoredBundle = null;
472 FileInputStream inFile = null;
473 try {
474 inFile = new FileInputStream(
475 new File(mContext.getFilesDir(), getFilenameForConfig(packageName, iccid)));
476 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
477 parser.setInput(inFile, "utf-8");
478
479 int event;
480 while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
481
482 if (event == XmlPullParser.START_TAG && TAG_VERSION.equals(parser.getName())) {
483 String savedVersion = parser.nextText();
484 if (!version.equals(savedVersion)) {
485 log("Saved version mismatch: " + version + " vs " + savedVersion);
486 break;
487 }
488 }
489
490 if (event == XmlPullParser.START_TAG && TAG_BUNDLE.equals(parser.getName())) {
491 restoredBundle = PersistableBundle.restoreFromXml(parser);
492 }
493 }
494 inFile.close();
495 }
496 catch (FileNotFoundException e) {
497 loge(e.toString());
498 }
499 catch (XmlPullParserException e) {
500 loge(e.toString());
501 }
502 catch (IOException e) {
503 loge(e.toString());
504 }
505
506 return restoredBundle;
507 }
508
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700509 /** Deletes all saved XML files associated with the given package name. */
510 private void deleteConfigForPackage(final String packageName) {
511 File dir = mContext.getFilesDir();
512 File[] packageFiles = dir.listFiles(new FilenameFilter() {
513 public boolean accept(File dir, String filename) {
514 return filename.startsWith("carrierconfig-" + packageName + "-");
515 }
516 });
517 for (File f : packageFiles) {
518 log("deleting " + f.getName());
519 f.delete();
520 }
521 }
522
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700523 /** Builds a canonical file name for a config file. */
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700524 private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid) {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700525 return "carrierconfig-" + packageName + "-" + iccid + ".xml";
526 }
527
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700528 /** Return the current version code of a package, or null if the name is not found. */
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700529 private String getPackageVersion(String packageName) {
530 try {
531 PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700532 return Integer.toString(info.versionCode);
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700533 } catch (PackageManager.NameNotFoundException e) {
534 return null;
535 }
536 }
537
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700538 /** Read up to date config.
539 *
540 * This reads config bundles for the given phoneId. That means getting the latest bundle from
541 * the default app and a privileged carrier app, if present. This will not bind to an app if we
542 * have a saved config file to use instead.
543 */
544 private void updateConfigForPhoneId(int phoneId) {
545 mHandler.sendMessage(mHandler.obtainMessage(EVENT_FETCH_DEFAULT, phoneId, -1));
546 }
547
548 @Override public
549 @NonNull
550 PersistableBundle getConfigForSubId(int subId) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800551 int phoneId = SubscriptionManager.getPhoneId(subId);
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700552 PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig();
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800553 if (SubscriptionManager.isValidPhoneId(phoneId)) {
Jonathan Basseric31f1f32015-05-12 10:13:03 -0700554 PersistableBundle config = mConfigFromDefaultApp[phoneId];
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700555 if (config != null)
556 retConfig.putAll(config);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800557 config = mConfigFromCarrierApp[phoneId];
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700558 if (config != null)
559 retConfig.putAll(config);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800560 }
561 return retConfig;
562 }
563
564 @Override
565 public void reloadCarrierConfigForSubId(int subId) {
566 int phoneId = SubscriptionManager.getPhoneId(subId);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700567 if (!SubscriptionManager.isValidPhoneId(phoneId)) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800568 log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700569 return;
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800570 }
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700571 String callingPackageName = mContext.getPackageManager().getNameForUid(
572 Binder.getCallingUid());
573 // TODO: Check that the calling packages is privileged for subId specifically.
574 int privilegeStatus = TelephonyManager.from(mContext).checkCarrierPrivilegesForPackage(
575 callingPackageName);
576 if (privilegeStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
577 throw new SecurityException(
578 "Package is not privileged for subId=" + subId + ": " + callingPackageName);
579 }
580
581 // This method should block until deleting has completed, so that an error which prevents us
582 // from clearing the cache is passed back to the carrier app. With the files successfully
583 // deleted, this can return and we will eventually bind to the carrier app.
584 deleteConfigForPackage(callingPackageName);
585 updateConfigForPhoneId(phoneId);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800586 }
587
588 @Override
589 public void updateConfigForPhoneId(int phoneId, String simState) {
590 log("update config for phoneId: " + phoneId + " simState: " + simState);
591 if (!SubscriptionManager.isValidPhoneId(phoneId)) {
592 return;
593 }
594 // requires Java 7 for switch on string.
595 switch (simState) {
596 case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
597 case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR:
598 case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN:
Junda Liu9f2d2712015-05-15 14:22:45 -0700599 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CLEAR_CONFIG, phoneId, -1));
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800600 break;
601 case IccCardConstants.INTENT_VALUE_ICC_LOADED:
602 case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700603 updateConfigForPhoneId(phoneId);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800604 break;
605 }
606 }
607
Junda Liu43d723a2015-05-12 17:23:45 -0700608 @Override
609 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
610 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
611 != PackageManager.PERMISSION_GRANTED) {
612 pw.println("Permission Denial: can't dump carrierconfig from from pid="
613 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
614 return;
615 }
616 pw.println("CarrierConfigLoader: " + this);
617 for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
618 pw.println(" Phone Id=" + i);
619 pw.println(" mConfigFromDefaultApp=" + mConfigFromDefaultApp[i]);
620 pw.println(" mConfigFromCarrierApp=" + mConfigFromCarrierApp[i]);
621 }
622 }
623
Zach Johnson36d7aab2015-05-22 15:43:00 -0700624 private class CarrierServiceConnection implements ServiceConnection {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800625 int phoneId;
626 int eventId;
627 IBinder service;
628
Zach Johnson36d7aab2015-05-22 15:43:00 -0700629 public CarrierServiceConnection(int phoneId, int eventId) {
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800630 this.phoneId = phoneId;
631 this.eventId = eventId;
632 }
633
634 @Override
635 public void onServiceConnected(ComponentName name, IBinder service) {
636 log("Connected to config app: " + name.flattenToString());
637 this.service = service;
638 mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this));
639 }
640
641 @Override
642 public void onServiceDisconnected(ComponentName name) {
643 this.service = null;
644 }
645 }
646
647 private class ConfigLoaderBroadcastReceiver extends BroadcastReceiver {
648 @Override
649 public void onReceive(Context context, Intent intent) {
650 String action = intent.getAction();
651 log("Receive action: " + action);
Jonathan Basseri1f743c92015-05-15 00:19:46 -0700652 switch (action) {
653 case Intent.ACTION_PACKAGE_ADDED:
654 case Intent.ACTION_PACKAGE_CHANGED:
655 case Intent.ACTION_PACKAGE_REMOVED:
656 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
657 String packageName = mContext.getPackageManager().getNameForUid(uid);
658 // We don't have a phoneId for arg1.
659 mHandler.sendMessage(
660 mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
661 break;
662
663 }
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800664 }
665 }
666
667 private static void log(String msg) {
Jonathan Basseri6b50e9f2015-05-12 20:18:31 -0700668 Log.d(LOG_TAG, msg);
669 }
670
671 private static void loge(String msg) {
672 Log.e(LOG_TAG, msg);
Jonathan Basseri6465afd2015-02-25 13:05:57 -0800673 }
674}