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