PhoneFactory.getPhone()引發(fā)的問題解決

最近產(chǎn)品提出來了一個(gè)需求,就是將來電轉(zhuǎn)接這個(gè)功能的入口往外移。之后我就通過adb命令,獲知來電界面屬于GsmUmtsCallForwardOptions類。之后查看源碼,再將這塊代碼弄得一知半解的時(shí)候,我將來電轉(zhuǎn)接這塊的代碼移到自己代碼中,一運(yùn)行直接報(bào)錯(cuò)。查看日志如下:

AndroidRuntime: Caused by: java.lang.IllegalStateException: Default phones haven't been made yet!
AndroidRuntime: at com.android.internal.telephony.PhoneFactory.getPhone(PhoneFactory.java:510)

PhoneFactory.getPhone方法如下,sMadeDefaults是一個(gè)Boolean值,當(dāng)為false的時(shí)候,就會直接報(bào)出異常,跟之前報(bào)錯(cuò)所抓的日志一致。

 public static Phone getPhone(int phoneId) {
        Phone phone;
        String dbgInfo = "";

        synchronized (sLockProxyPhones) {
            if (!sMadeDefaults) {
                throw new IllegalStateException("Default phones haven't been made yet!");
                // CAF_MSIM FIXME need to introduce default phone id ?
            } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
                if (DBG) dbgInfo = "phoneId == DEFAULT_PHONE_ID return sProxyPhone";
                phone = sProxyPhone;
            } else {
                if (DBG) dbgInfo = "phoneId != DEFAULT_PHONE_ID return sProxyPhones[phoneId]";
                phone = (((phoneId >= 0)
                                && (phoneId < TelephonyManager.getDefault().getPhoneCount()))
                        ? sProxyPhones[phoneId] : null);
            }
            if (DBG) {
                Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId +
                        " phone=" + phone);
            }
            return phone;
        }
/**
     * FIXME replace this with some other way of making these
     * instances
     */
    public static void makeDefaultPhone(Context context) {
        synchronized (sLockProxyPhones) {
            if (!sMadeDefaults) {
                sContext = context;

                // create the telephony device controller.
                TelephonyDevController.create();

                int retryCount = 0;
                for(;;) {
                    boolean hasException = false;
                    retryCount ++;

                    try {
                        // use UNIX domain socket to
                        // prevent subsequent initialization
                        new LocalServerSocket("com.android.internal.telephony");
                    } catch (java.io.IOException ex) {
                        hasException = true;
                    }

                    if ( !hasException ) {
                        break;
                    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
                        throw new RuntimeException("PhoneFactory probably already running");
                    } else {
                        try {
                            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
                        } catch (InterruptedException er) {
                        }
                    }
                }

                sPhoneNotifier = new DefaultPhoneNotifier();

                int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context);
                Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);

                /* In case of multi SIM mode two instances of PhoneProxy, RIL are created,
                   where as in single SIM mode only instance. isMultiSimEnabled() function checks
                   whether it is single SIM or multi SIM mode */
                int numPhones = TelephonyManager.getDefault().getPhoneCount();
                int[] networkModes = new int[numPhones];
                sProxyPhones = new PhoneProxy[numPhones];
                sCommandsInterfaces = new RIL[numPhones];

                for (int i = 0; i < numPhones; i++) {
                    // reads the system properties and makes commandsinterface
                    // Get preferred network type.
                    networkModes[i] = RILConstants.PREFERRED_NETWORK_MODE;

                    Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkModes[i]));
                    sCommandsInterfaces[i] = new RIL(context, networkModes[i],
                            cdmaSubscription, i);
                }
                Rlog.i(LOG_TAG, "Creating SubscriptionController");
                SubscriptionController.init(context, sCommandsInterfaces);

                // Instantiate UiccController so that all other classes can just
                // call getInstance()
                mUiccController = UiccController.make(context, sCommandsInterfaces);

                for (int i = 0; i < numPhones; i++) {
                    PhoneBase phone = null;
                    int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
                    if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                        phone = new GSMPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
                        phone.startMonitoringImsService();
                    } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                        phone = new CDMALTEPhone(context,
                                sCommandsInterfaces[i], sPhoneNotifier, i);
                        phone.startMonitoringImsService();
                    }
                    Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);

                    sProxyPhones[i] = new PhoneProxy(phone);
                }
                mProxyController = ProxyController.getInstance(context, sProxyPhones,
                        mUiccController, sCommandsInterfaces);

                // Set the default phone in base class.
                // FIXME: This is a first best guess at what the defaults will be. It
                // FIXME: needs to be done in a more controlled manner in the future.
                sProxyPhone = sProxyPhones[0];
                sCommandsInterface = sCommandsInterfaces[0];

                // Ensure that we have a default SMS app. Requesting the app with
                // updateIfNeeded set to true is enough to configure a default SMS app.
                ComponentName componentName =
                        SmsApplication.getDefaultSmsApplication(context, true /* updateIfNeeded */);
                String packageName = "NONE";
                if (componentName != null) {
                    packageName = componentName.getPackageName();
                }
                Rlog.i(LOG_TAG, "defaultSmsApplication: " + packageName);

                // Set up monitor to watch for changes to SMS packages
                SmsApplication.initSmsPackageMonitor(context);

                sMadeDefaults = true;

                Rlog.i(LOG_TAG, "Creating SubInfoRecordUpdater ");
                sSubInfoRecordUpdater = new SubscriptionInfoUpdater(context,
                        sProxyPhones, sCommandsInterfaces);
                SubscriptionController.getInstance().updatePhonesAvailability(sProxyPhones);
            }
        }
    }

PhoneFactory中只有makeDefaultPhone方法會改變sMadeDefaults布爾值。于是我就主動(dòng)調(diào)用了PhoneFactory的makeDefaultPhone方法,但是會出現(xiàn)異常,java.lang.RuntimeException: failed to set system property,屬于底層報(bào)錯(cuò),這一下懵逼了。
通過在網(wǎng)上查找資料發(fā)現(xiàn),在com.android.phone.PhoneApp中的onCreate方法中調(diào)用了,PhoneApp的源碼位于(packages\services\telephony\src\com\android\phone),在AndroidManifest.xml中的文件配置如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        package="com.android.phone"
        android:versionCode="2"
        android:versionName="02.01.1608231404"
        coreApp="true"
        android:sharedUserId="android.uid.phone"
        android:sharedUserLabel="@string/phoneAppLabel"
>

對于android:persistent="true" 的應(yīng)用是在Android開機(jī)時(shí)啟動(dòng)的,PhoneApp是com,android.phone的Application,所以按理說一開機(jī)的時(shí)候就會調(diào)用PhoneFactory中的makeDefaultPhone方法。所以可以通過共享com.android.phone中的PhoneFactory對象調(diào)用getPhone方法。

修改AndroidManifest.xml 文件,具體修改如下,注意android:sharedUserId="android.uid.phone" 和android:process="com.android.phone"部分,之后進(jìn)行簽名。

相同的shareduserid指可以訪問對方apk的數(shù)據(jù)庫,文件等,若需要訪問內(nèi)存中的數(shù)據(jù),則需要跑在相同的進(jìn)程中。指定process則可以使其跑在同一進(jìn)程中,才可以獲取進(jìn)程內(nèi)的數(shù)據(jù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容