CarrierConfigLoader.java revision 66d9f2c1c93dfc9858c52acbb88b6fca0c5e2cfc
1/** 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 17package com.android.phone; 18 19import static android.Manifest.permission.READ_PHONE_STATE; 20import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; 21 22import android.annotation.NonNull; 23import 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; 30import android.content.SharedPreferences; 31import android.content.pm.PackageInfo; 32import android.content.pm.PackageManager; 33import android.database.sqlite.SQLiteDatabase; 34import android.database.sqlite.SQLiteOpenHelper; 35import android.os.AsyncResult; 36import android.os.Binder; 37import android.os.Build; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Message; 41import android.os.PersistableBundle; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.UserHandle; 45import android.preference.PreferenceManager; 46import android.service.carrier.CarrierIdentifier; 47import android.service.carrier.CarrierService; 48import android.service.carrier.ICarrierService; 49import android.telephony.CarrierConfigManager; 50import android.telephony.SubscriptionManager; 51import android.telephony.TelephonyManager; 52import android.util.Log; 53 54import com.android.internal.telephony.ICarrierConfigLoader; 55import com.android.internal.telephony.IccCardConstants; 56import com.android.internal.telephony.Phone; 57import com.android.internal.telephony.PhoneConstants; 58import com.android.internal.telephony.PhoneFactory; 59import com.android.internal.telephony.TelephonyIntents; 60import com.android.internal.util.FastXmlSerializer; 61 62import org.xmlpull.v1.XmlPullParser; 63import org.xmlpull.v1.XmlPullParserException; 64import org.xmlpull.v1.XmlPullParserFactory; 65 66import java.io.File; 67import java.io.FileDescriptor; 68import java.io.FileInputStream; 69import java.io.FileNotFoundException; 70import java.io.FileOutputStream; 71import java.io.FilenameFilter; 72import java.io.IOException; 73import java.io.PrintWriter; 74import java.util.List; 75 76/** 77 * CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays. 78 */ 79 80public class CarrierConfigLoader extends ICarrierConfigLoader.Stub { 81 private static final String LOG_TAG = "CarrierConfigLoader"; 82 // Package name for default carrier config app, bundled with system image. 83 private static final String DEFAULT_CARRIER_CONFIG_PACKAGE = "com.android.carrierconfig"; 84 85 /** The singleton instance. */ 86 private static CarrierConfigLoader sInstance; 87 // The context for phone app, passed from PhoneGlobals. 88 private Context mContext; 89 // Carrier configs from default app, indexed by phoneID. 90 private PersistableBundle[] mConfigFromDefaultApp; 91 // Carrier configs from privileged carrier config app, indexed by phoneID. 92 private PersistableBundle[] mConfigFromCarrierApp; 93 // Service connection for binding to config app. 94 private CarrierServiceConnection[] mServiceConnection; 95 96 // Broadcast receiver for SIM and pkg intents, register intent filter in constructor. 97 private final BroadcastReceiver mReceiver = new ConfigLoaderBroadcastReceiver(); 98 99 // Message codes; see mHandler below. 100 // Request from SubscriptionInfoUpdater when SIM becomes absent or error. 101 private static final int EVENT_CLEAR_CONFIG = 0; 102 // Has connected to default app. 103 private static final int EVENT_CONNECTED_TO_DEFAULT = 3; 104 // Has connected to carrier app. 105 private static final int EVENT_CONNECTED_TO_CARRIER = 4; 106 // Config has been loaded from default app. 107 private static final int EVENT_LOADED_FROM_DEFAULT = 5; 108 // Config has been loaded from carrier app. 109 private static final int EVENT_LOADED_FROM_CARRIER = 6; 110 // Attempt to fetch from default app or read from XML. 111 private static final int EVENT_FETCH_DEFAULT = 7; 112 // Attempt to fetch from carrier app or read from XML. 113 private static final int EVENT_FETCH_CARRIER = 8; 114 // A package has been installed, uninstalled, or updated. 115 private static final int EVENT_PACKAGE_CHANGED = 9; 116 // Bind timed out for the default app. 117 private static final int EVENT_BIND_DEFAULT_TIMEOUT = 10; 118 // Bind timed out for a carrier app. 119 private static final int EVENT_BIND_CARRIER_TIMEOUT = 11; 120 // Check if the system fingerprint has changed. 121 private static final int EVENT_CHECK_SYSTEM_UPDATE = 12; 122 123 private static final int BIND_TIMEOUT_MILLIS = 10000; 124 125 // Tags used for saving and restoring XML documents. 126 private static final String TAG_DOCUMENT = "carrier_config"; 127 private static final String TAG_VERSION = "package_version"; 128 private static final String TAG_BUNDLE = "bundle_data"; 129 130 // SharedPreferences key for last known build fingerprint. 131 private static final String KEY_FINGERPRINT = "build_fingerprint"; 132 133 // Handler to process various events. 134 // 135 // For each phoneId, the event sequence should be: 136 // fetch default, connected to default, loaded from default, 137 // fetch carrier, connected to carrier, loaded from carrier. 138 // 139 // If there is a saved config file for either the default app or the carrier app, we skip 140 // binding to the app and go straight from fetch to loaded. 141 // 142 // At any time, at most one connection is active. If events are not in this order, previous 143 // connection will be unbound, so only latest event takes effect. 144 // 145 // We broadcast ACTION_CARRIER_CONFIG_CHANGED after: 146 // 1. loading from carrier app (even if read from a file) 147 // 2. loading from default app if there is no carrier app (even if read from a file) 148 // 3. clearing config (e.g. due to sim removal) 149 // 4. encountering bind or IPC error 150 private Handler mHandler = new Handler() { 151 @Override 152 public void handleMessage(Message msg) { 153 int phoneId = msg.arg1; 154 log("mHandler: " + msg.what + " phoneId: " + phoneId); 155 String iccid; 156 CarrierIdentifier carrierId; 157 String carrierPackageName; 158 CarrierServiceConnection conn; 159 PersistableBundle config; 160 switch (msg.what) { 161 case EVENT_CLEAR_CONFIG: 162 if (mConfigFromDefaultApp[phoneId] == null && 163 mConfigFromCarrierApp[phoneId] == null) 164 break; 165 mConfigFromDefaultApp[phoneId] = null; 166 mConfigFromCarrierApp[phoneId] = null; 167 mServiceConnection[phoneId] = null; 168 broadcastConfigChangedIntent(phoneId); 169 break; 170 171 case EVENT_PACKAGE_CHANGED: 172 carrierPackageName = (String) msg.obj; 173 // Only update if there are cached config removed to avoid updating config 174 // for unrelated packages. 175 if (clearCachedConfigForPackage(carrierPackageName)) { 176 int numPhones = TelephonyManager.from(mContext).getPhoneCount(); 177 for (int i = 0; i < numPhones; ++i) { 178 updateConfigForPhoneId(i); 179 } 180 } 181 break; 182 183 case EVENT_FETCH_DEFAULT: 184 iccid = getIccIdForPhoneId(phoneId); 185 config = restoreConfigFromXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid); 186 if (config != null) { 187 log("Loaded config from XML. package=" + DEFAULT_CARRIER_CONFIG_PACKAGE 188 + " phoneId=" + phoneId); 189 mConfigFromDefaultApp[phoneId] = config; 190 Message newMsg = obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1); 191 newMsg.getData().putBoolean("loaded_from_xml", true); 192 mHandler.sendMessage(newMsg); 193 } else { 194 if (bindToConfigPackage(DEFAULT_CARRIER_CONFIG_PACKAGE, 195 phoneId, EVENT_CONNECTED_TO_DEFAULT)) { 196 sendMessageDelayed(obtainMessage(EVENT_BIND_DEFAULT_TIMEOUT, phoneId, -1), 197 BIND_TIMEOUT_MILLIS); 198 } else { 199 // Send bcast if bind fails 200 broadcastConfigChangedIntent(phoneId); 201 } 202 } 203 break; 204 205 case EVENT_CONNECTED_TO_DEFAULT: 206 removeMessages(EVENT_BIND_DEFAULT_TIMEOUT); 207 carrierId = getCarrierIdForPhoneId(phoneId); 208 conn = (CarrierServiceConnection) msg.obj; 209 // If new service connection has been created, unbind. 210 if (mServiceConnection[phoneId] != conn || conn.service == null) { 211 mContext.unbindService(conn); 212 break; 213 } 214 try { 215 ICarrierService carrierService = ICarrierService.Stub 216 .asInterface(conn.service); 217 config = carrierService.getCarrierConfig(carrierId); 218 iccid = getIccIdForPhoneId(phoneId); 219 saveConfigToXml(DEFAULT_CARRIER_CONFIG_PACKAGE, iccid, config); 220 mConfigFromDefaultApp[phoneId] = config; 221 sendMessage(obtainMessage(EVENT_LOADED_FROM_DEFAULT, phoneId, -1)); 222 } catch (Exception ex) { 223 // The bound app could throw exceptions that binder will pass to us. 224 loge("Failed to get carrier config: " + ex.toString()); 225 } finally { 226 mContext.unbindService(mServiceConnection[phoneId]); 227 } 228 break; 229 230 case EVENT_BIND_DEFAULT_TIMEOUT: 231 mContext.unbindService(mServiceConnection[phoneId]); 232 broadcastConfigChangedIntent(phoneId); 233 break; 234 235 case EVENT_LOADED_FROM_DEFAULT: 236 // If we attempted to bind to the app, but the service connection is null, then 237 // config was cleared while we were waiting and we should not continue. 238 if (!msg.getData().getBoolean("loaded_from_xml", false) 239 && mServiceConnection[phoneId] == null) { 240 break; 241 } 242 carrierPackageName = getCarrierPackageForPhoneId(phoneId); 243 if (carrierPackageName != null) { 244 log("Found carrier config app: " + carrierPackageName); 245 sendMessage(obtainMessage(EVENT_FETCH_CARRIER, phoneId)); 246 } else { 247 broadcastConfigChangedIntent(phoneId); 248 } 249 break; 250 251 case EVENT_FETCH_CARRIER: 252 carrierPackageName = getCarrierPackageForPhoneId(phoneId); 253 iccid = getIccIdForPhoneId(phoneId); 254 config = restoreConfigFromXml(carrierPackageName, iccid); 255 if (config != null) { 256 log("Loaded config from XML. package=" + carrierPackageName + " phoneId=" 257 + phoneId); 258 mConfigFromCarrierApp[phoneId] = config; 259 Message newMsg = obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1); 260 newMsg.getData().putBoolean("loaded_from_xml", true); 261 sendMessage(newMsg); 262 } else { 263 if (carrierPackageName != null 264 && bindToConfigPackage(carrierPackageName, phoneId, 265 EVENT_CONNECTED_TO_CARRIER)) { 266 sendMessageDelayed(obtainMessage(EVENT_BIND_CARRIER_TIMEOUT, phoneId, -1), 267 BIND_TIMEOUT_MILLIS); 268 } else { 269 // Send bcast if bind fails 270 broadcastConfigChangedIntent(phoneId); 271 } 272 } 273 break; 274 275 case EVENT_CONNECTED_TO_CARRIER: 276 removeMessages(EVENT_BIND_CARRIER_TIMEOUT); 277 carrierId = getCarrierIdForPhoneId(phoneId); 278 conn = (CarrierServiceConnection) msg.obj; 279 // If new service connection has been created, unbind. 280 if (mServiceConnection[phoneId] != conn || 281 conn.service == null) { 282 mContext.unbindService(conn); 283 break; 284 } 285 try { 286 ICarrierService carrierService = ICarrierService.Stub 287 .asInterface(conn.service); 288 config = carrierService.getCarrierConfig(carrierId); 289 carrierPackageName = getCarrierPackageForPhoneId(phoneId); 290 iccid = getIccIdForPhoneId(phoneId); 291 saveConfigToXml(carrierPackageName, iccid, config); 292 mConfigFromCarrierApp[phoneId] = config; 293 sendMessage(obtainMessage(EVENT_LOADED_FROM_CARRIER, phoneId, -1)); 294 } catch (Exception ex) { 295 // The bound app could throw exceptions that binder will pass to us. 296 loge("Failed to get carrier config: " + ex.toString()); 297 } finally { 298 mContext.unbindService(mServiceConnection[phoneId]); 299 } 300 break; 301 302 case EVENT_BIND_CARRIER_TIMEOUT: 303 mContext.unbindService(mServiceConnection[phoneId]); 304 broadcastConfigChangedIntent(phoneId); 305 break; 306 307 case EVENT_LOADED_FROM_CARRIER: 308 // If we attempted to bind to the app, but the service connection is null, then 309 // config was cleared while we were waiting and we should not continue. 310 if (!msg.getData().getBoolean("loaded_from_xml", false) 311 && mServiceConnection[phoneId] == null) { 312 break; 313 } 314 broadcastConfigChangedIntent(phoneId); 315 break; 316 317 case EVENT_CHECK_SYSTEM_UPDATE: 318 SharedPreferences sharedPrefs = 319 PreferenceManager.getDefaultSharedPreferences(mContext); 320 final String lastFingerprint = sharedPrefs.getString(KEY_FINGERPRINT, null); 321 if (!Build.FINGERPRINT.equals(lastFingerprint)) { 322 log("Build fingerprint changed. old: " 323 + lastFingerprint + " new: " + Build.FINGERPRINT); 324 clearCachedConfigForPackage(null); 325 sharedPrefs.edit().putString(KEY_FINGERPRINT, Build.FINGERPRINT).apply(); 326 } 327 break; 328 } 329 } 330 }; 331 332 /** 333 * Constructs a CarrierConfigLoader, registers it as a service, and registers a broadcast 334 * receiver for relevant events. 335 */ 336 private CarrierConfigLoader(Context context) { 337 mContext = context; 338 339 // Register for package updates. Update app or uninstall app update will have all 3 intents, 340 // in the order or removed, added, replaced, all with extra_replace set to true. 341 IntentFilter pkgFilter = new IntentFilter(); 342 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 343 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 344 pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 345 pkgFilter.addDataScheme("package"); 346 context.registerReceiverAsUser(mReceiver, UserHandle.ALL, pkgFilter, null, null); 347 348 int numPhones = TelephonyManager.from(context).getPhoneCount(); 349 mConfigFromDefaultApp = new PersistableBundle[numPhones]; 350 mConfigFromCarrierApp = new PersistableBundle[numPhones]; 351 mServiceConnection = new CarrierServiceConnection[numPhones]; 352 // Make this service available through ServiceManager. 353 ServiceManager.addService(Context.CARRIER_CONFIG_SERVICE, this); 354 log("CarrierConfigLoader has started"); 355 mHandler.sendEmptyMessage(EVENT_CHECK_SYSTEM_UPDATE); 356 } 357 358 /** 359 * Initialize the singleton CarrierConfigLoader instance. 360 * 361 * This is only done once, at startup, from {@link com.android.phone.PhoneApp#onCreate}. 362 */ 363 /* package */ 364 static CarrierConfigLoader init(Context context) { 365 synchronized (CarrierConfigLoader.class) { 366 if (sInstance == null) { 367 sInstance = new CarrierConfigLoader(context); 368 } else { 369 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 370 } 371 return sInstance; 372 } 373 } 374 375 private void broadcastConfigChangedIntent(int phoneId) { 376 Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 377 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 378 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId); 379 ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE, 380 UserHandle.USER_ALL); 381 } 382 383 /** Binds to the default or carrier config app. */ 384 private boolean bindToConfigPackage(String pkgName, int phoneId, int eventId) { 385 log("Binding to " + pkgName + " for phone " + phoneId); 386 Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE); 387 carrierService.setPackage(pkgName); 388 mServiceConnection[phoneId] = new CarrierServiceConnection(phoneId, eventId); 389 try { 390 return mContext.bindService(carrierService, mServiceConnection[phoneId], 391 Context.BIND_AUTO_CREATE); 392 } catch (SecurityException ex) { 393 return false; 394 } 395 } 396 397 private CarrierIdentifier getCarrierIdForPhoneId(int phoneId) { 398 String mcc = ""; 399 String mnc = ""; 400 String imsi = ""; 401 String gid1 = ""; 402 String gid2 = ""; 403 String spn = TelephonyManager.from(mContext).getSimOperatorNameForPhone(phoneId); 404 String simOperator = TelephonyManager.from(mContext).getSimOperatorNumericForPhone(phoneId); 405 // A valid simOperator should be 5 or 6 digits, depending on the length of the MNC. 406 if (simOperator != null && simOperator.length() >= 3) { 407 mcc = simOperator.substring(0, 3); 408 mnc = simOperator.substring(3); 409 } 410 Phone phone = PhoneFactory.getPhone(phoneId); 411 if (phone != null) { 412 imsi = phone.getSubscriberId(); 413 gid1 = phone.getGroupIdLevel1(); 414 gid2 = phone.getGroupIdLevel2(); 415 } 416 417 return new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2); 418 } 419 420 /** Returns the package name of a priveleged carrier app, or null if there is none. */ 421 private String getCarrierPackageForPhoneId(int phoneId) { 422 List<String> carrierPackageNames = TelephonyManager.from(mContext) 423 .getCarrierPackageNamesForIntentAndPhone( 424 new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId); 425 if (carrierPackageNames != null && carrierPackageNames.size() > 0) { 426 return carrierPackageNames.get(0); 427 } else { 428 return null; 429 } 430 } 431 432 private String getIccIdForPhoneId(int phoneId) { 433 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 434 return null; 435 } 436 Phone phone = PhoneFactory.getPhone(phoneId); 437 if (phone == null) { 438 return null; 439 } 440 return phone.getIccSerialNumber(); 441 } 442 443 /** 444 * Writes a bundle to an XML file. 445 * 446 * The bundle will be written to a file named after the package name and ICCID, so that it can 447 * be restored later with {@link @restoreConfigFromXml}. The XML output will include the bundle 448 * and the current version of the specified package. 449 * 450 * In case of errors or invalid input, no file will be written. 451 * 452 * @param packageName the name of the package from which we fetched this bundle. 453 * @param iccid the ICCID of the subscription for which this bundle was fetched. 454 * @param config the bundle to be written. Null will be treated as an empty bundle. 455 */ 456 private void saveConfigToXml(String packageName, String iccid, PersistableBundle config) { 457 if (packageName == null || iccid == null) { 458 loge("Cannot save config with null packageName or iccid."); 459 return; 460 } 461 if (config == null) { 462 config = new PersistableBundle(); 463 } 464 465 final String version = getPackageVersion(packageName); 466 if (version == null) { 467 loge("Failed to get package version for: " + packageName); 468 return; 469 } 470 471 FileOutputStream outFile = null; 472 try { 473 outFile = new FileOutputStream( 474 new File(mContext.getFilesDir(), getFilenameForConfig(packageName, iccid))); 475 FastXmlSerializer out = new FastXmlSerializer(); 476 out.setOutput(outFile, "utf-8"); 477 out.startDocument("utf-8", true); 478 out.startTag(null, TAG_DOCUMENT); 479 out.startTag(null, TAG_VERSION); 480 out.text(version); 481 out.endTag(null, TAG_VERSION); 482 out.startTag(null, TAG_BUNDLE); 483 config.saveToXml(out); 484 out.endTag(null, TAG_BUNDLE); 485 out.endTag(null, TAG_DOCUMENT); 486 out.endDocument(); 487 out.flush(); 488 outFile.close(); 489 } 490 catch (IOException e) { 491 loge(e.toString()); 492 } 493 catch (XmlPullParserException e) { 494 loge(e.toString()); 495 } 496 } 497 498 /** 499 * Reads a bundle from an XML file. 500 * 501 * This restores a bundle that was written with {@link #saveConfigToXml}. This returns the saved 502 * config bundle for the given package and ICCID. 503 * 504 * In case of errors, or if the saved config is from a different package version than the 505 * current version, then null will be returned. 506 * 507 * @param packageName the name of the package from which we fetched this bundle. 508 * @param iccid the ICCID of the subscription for which this bundle was fetched. 509 * @return the bundle from the XML file. Returns null if there is no saved config, the saved 510 * version does not match, or reading config fails. 511 */ 512 private PersistableBundle restoreConfigFromXml(String packageName, String iccid) { 513 final String version = getPackageVersion(packageName); 514 if (version == null) { 515 loge("Failed to get package version for: " + packageName); 516 return null; 517 } 518 if (packageName == null || iccid == null) { 519 loge("Cannot restore config with null packageName or iccid."); 520 return null; 521 } 522 523 PersistableBundle restoredBundle = null; 524 FileInputStream inFile = null; 525 try { 526 inFile = new FileInputStream( 527 new File(mContext.getFilesDir(), getFilenameForConfig(packageName, iccid))); 528 XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); 529 parser.setInput(inFile, "utf-8"); 530 531 int event; 532 while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) { 533 534 if (event == XmlPullParser.START_TAG && TAG_VERSION.equals(parser.getName())) { 535 String savedVersion = parser.nextText(); 536 if (!version.equals(savedVersion)) { 537 log("Saved version mismatch: " + version + " vs " + savedVersion); 538 break; 539 } 540 } 541 542 if (event == XmlPullParser.START_TAG && TAG_BUNDLE.equals(parser.getName())) { 543 restoredBundle = PersistableBundle.restoreFromXml(parser); 544 } 545 } 546 inFile.close(); 547 } 548 catch (FileNotFoundException e) { 549 loge(e.toString()); 550 } 551 catch (XmlPullParserException e) { 552 loge(e.toString()); 553 } 554 catch (IOException e) { 555 loge(e.toString()); 556 } 557 558 return restoredBundle; 559 } 560 561 /** 562 * Clears cached carrier config. 563 * This deletes all saved XML files associated with the given package name. If packageName is 564 * null, then it deletes all saved XML files. 565 * 566 * @param packageName the name of a carrier package, or null if all cached config should be 567 * cleared. 568 * @return true iff one or more files were deleted. 569 */ 570 private boolean clearCachedConfigForPackage(final String packageName) { 571 File dir = mContext.getFilesDir(); 572 File[] packageFiles = dir.listFiles(new FilenameFilter() { 573 public boolean accept(File dir, String filename) { 574 if (packageName != null) { 575 return filename.startsWith("carrierconfig-" + packageName + "-"); 576 } else { 577 return filename.startsWith("carrierconfig-"); 578 } 579 } 580 }); 581 if (packageFiles == null || packageFiles.length < 1) return false; 582 for (File f : packageFiles) { 583 log("deleting " + f.getName()); 584 f.delete(); 585 } 586 return true; 587 } 588 589 /** Builds a canonical file name for a config file. */ 590 private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid) { 591 return "carrierconfig-" + packageName + "-" + iccid + ".xml"; 592 } 593 594 /** Return the current version code of a package, or null if the name is not found. */ 595 private String getPackageVersion(String packageName) { 596 try { 597 PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0); 598 return Integer.toString(info.versionCode); 599 } catch (PackageManager.NameNotFoundException e) { 600 return null; 601 } 602 } 603 604 /** Read up to date config. 605 * 606 * This reads config bundles for the given phoneId. That means getting the latest bundle from 607 * the default app and a privileged carrier app, if present. This will not bind to an app if we 608 * have a saved config file to use instead. 609 */ 610 private void updateConfigForPhoneId(int phoneId) { 611 // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no 612 // stale config is left. 613 if (mConfigFromCarrierApp[phoneId] != null && 614 getCarrierPackageForPhoneId(phoneId) == null) { 615 mConfigFromCarrierApp[phoneId] = null; 616 } 617 mHandler.sendMessage(mHandler.obtainMessage(EVENT_FETCH_DEFAULT, phoneId, -1)); 618 } 619 620 @Override public 621 @NonNull 622 PersistableBundle getConfigForSubId(int subId) { 623 try { 624 mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, null); 625 // SKIP checking run-time READ_PHONE_STATE since using PRIVILEGED 626 } catch (SecurityException e) { 627 mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, null); 628 } 629 int phoneId = SubscriptionManager.getPhoneId(subId); 630 PersistableBundle retConfig = CarrierConfigManager.getDefaultConfig(); 631 if (SubscriptionManager.isValidPhoneId(phoneId)) { 632 PersistableBundle config = mConfigFromDefaultApp[phoneId]; 633 if (config != null) 634 retConfig.putAll(config); 635 config = mConfigFromCarrierApp[phoneId]; 636 if (config != null) 637 retConfig.putAll(config); 638 } 639 return retConfig; 640 } 641 642 @Override 643 public void notifyConfigChangedForSubId(int subId) { 644 int phoneId = SubscriptionManager.getPhoneId(subId); 645 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 646 log("Ignore invalid phoneId: " + phoneId + " for subId: " + subId); 647 return; 648 } 649 String callingPackageName = mContext.getPackageManager().getNameForUid( 650 Binder.getCallingUid()); 651 // TODO: Check that the calling packages is privileged for subId specifically. 652 int privilegeStatus = TelephonyManager.from(mContext).checkCarrierPrivilegesForPackage( 653 callingPackageName); 654 if (privilegeStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 655 throw new SecurityException( 656 "Package is not privileged for subId=" + subId + ": " + callingPackageName); 657 } 658 659 // This method should block until deleting has completed, so that an error which prevents us 660 // from clearing the cache is passed back to the carrier app. With the files successfully 661 // deleted, this can return and we will eventually bind to the carrier app. 662 clearCachedConfigForPackage(callingPackageName); 663 updateConfigForPhoneId(phoneId); 664 } 665 666 @Override 667 public void updateConfigForPhoneId(int phoneId, String simState) { 668 mContext.enforceCallingOrSelfPermission( 669 android.Manifest.permission.MODIFY_PHONE_STATE, null); 670 log("update config for phoneId: " + phoneId + " simState: " + simState); 671 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 672 return; 673 } 674 // requires Java 7 for switch on string. 675 switch (simState) { 676 case IccCardConstants.INTENT_VALUE_ICC_ABSENT: 677 case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: 678 case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN: 679 mHandler.sendMessage(mHandler.obtainMessage(EVENT_CLEAR_CONFIG, phoneId, -1)); 680 break; 681 case IccCardConstants.INTENT_VALUE_ICC_LOADED: 682 case IccCardConstants.INTENT_VALUE_ICC_LOCKED: 683 updateConfigForPhoneId(phoneId); 684 break; 685 } 686 } 687 688 @Override 689 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 690 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 691 != PackageManager.PERMISSION_GRANTED) { 692 pw.println("Permission Denial: can't dump carrierconfig from from pid=" 693 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); 694 return; 695 } 696 pw.println("CarrierConfigLoader: " + this); 697 for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) { 698 pw.println(" Phone Id=" + i); 699 pw.println(" mConfigFromDefaultApp=" + mConfigFromDefaultApp[i]); 700 pw.println(" mConfigFromCarrierApp=" + mConfigFromCarrierApp[i]); 701 } 702 } 703 704 private class CarrierServiceConnection implements ServiceConnection { 705 int phoneId; 706 int eventId; 707 IBinder service; 708 709 public CarrierServiceConnection(int phoneId, int eventId) { 710 this.phoneId = phoneId; 711 this.eventId = eventId; 712 } 713 714 @Override 715 public void onServiceConnected(ComponentName name, IBinder service) { 716 log("Connected to config app: " + name.flattenToString()); 717 this.service = service; 718 mHandler.sendMessage(mHandler.obtainMessage(eventId, phoneId, -1, this)); 719 } 720 721 @Override 722 public void onServiceDisconnected(ComponentName name) { 723 this.service = null; 724 } 725 } 726 727 private class ConfigLoaderBroadcastReceiver extends BroadcastReceiver { 728 @Override 729 public void onReceive(Context context, Intent intent) { 730 String action = intent.getAction(); 731 boolean replace = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); 732 // If replace is true, only care ACTION_PACKAGE_REPLACED. 733 if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action)) 734 return; 735 736 switch (action) { 737 case Intent.ACTION_PACKAGE_ADDED: 738 case Intent.ACTION_PACKAGE_REMOVED: 739 case Intent.ACTION_PACKAGE_REPLACED: 740 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 741 String packageName = mContext.getPackageManager().getNameForUid(uid); 742 if (packageName != null) { 743 // We don't have a phoneId for arg1. 744 mHandler.sendMessage( 745 mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName)); 746 } 747 break; 748 } 749 } 750 } 751 752 private static void log(String msg) { 753 Log.d(LOG_TAG, msg); 754 } 755 756 private static void loge(String msg) { 757 Log.e(LOG_TAG, msg); 758 } 759} 760