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