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