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