Android Telephony分析(四) ---- TelephonyManager詳解


 

前言


TelephonyManager主要提供Telephony相關信息的查詢/修改功能,以及Phone狀態監聽功能,封裝的方法主要是提供給APP上層使用。
TelephonyManager.java 在frameworks\base\telephony\java\android\telephony目錄下。

 


1. TelephonyManager整體結構



從TelephonyManager導入的文件中可以發現有四個接口

import com.android.internal.telecom.ITelecomService;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;

分別對應下面這幾個AIDL接口:

\frameworks\base\telecomm\java\com\android\internal\telecom\ITelecomService.aidl
\frameworks\base\telephony\java\com\android\internal\telephony\IPhoneSubInfo.aidl
\frameworks\base\telephony\java\com\android\internal\telephony\ITelephony.aidl
\frameworks\base\telephony\java\com\android\internal\telephony\ITelephonyRegistry.aidl

以可以猜測到,在TelephonyManager中可以得到這四種Service。
通過所有文件中搜索”extends 接口名.Stub”,如”extends ITelephony.Stub”,可以找到是哪些類實現了上面四個AIDL接口中的方法,整理可得:

在TelephonyManager中搜索”接口名.Stub.asInterface”,如”ITelephony.Stub.asInterface”,可以找到這四個Service的名字,整理可得:

 

 private ITelecomService getTelecomService() {
     return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
    }
@UnsupportedAppUsage
     private IPhoneSubInfo getSubscriberInfo() {
       // get it each time because that process crashes a lot
         return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
     }
private ITelephony getITelephony() {
      return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
   }
 private ITelephonyRegistry getTelephonyRegistry() {
      return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
   }

 

好了,下面分別對這四種Service進行分析:

 

1.1 TelecomServiceImpl—Telecom Service(TelecomLoaderService.java注冊)


服務端TelecomServiceImpl中有mBinderImpl實現了ITelecomService接口中的方法

public class TelecomServiceImpl {
    private final ITelecomService.Stub mBinderImpl = new ITelecomService.Stub() {
        ...
    }
}

在TelecomLoaderService.java中,TelecomServiceImpl把自己注冊到ServiceManager中,

    ServiceManager.addService(Context.TELECOM_SERVICE, service);

所以在TelephonyManager中可以通過ServiceManager得到Telecom Service

  private ITelecomService getTelecomService() {
        //得到TelecomServiceImpl的代理對象
    return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
  }

其實Telecom Service的最常用客戶端是TelecomManager.java。而在TelephonyManager中由於無法得到CallManager對象,所以只能依賴Telecom Service獲取Call State。

    /**
     * Returns one of the following constants that represents the current state of all
     * phone calls.
     *
     * {@link TelephonyManager#CALL_STATE_RINGING}
     * {@link TelephonyManager#CALL_STATE_OFFHOOK}
     * {@link TelephonyManager#CALL_STATE_IDLE}
     */
    public int getCallState() {
        try {
            ITelecomService telecom = getTelecomService();
            if (telecom != null) {
                return telecom.getCallState();
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling ITelecomService#getCallState", e);
        }
        return CALL_STATE_IDLE;
    }

所以,總體上來說,雖然TelecomManager得到了Telecom Service,但其實作用不大。相反,Telecom Service中會反過來得到TelephonyManager對象,進一步實現自己的方法,如在TelecomServiceImpl.java中:

    public String getVoiceMailNumber(PhoneAccountHandle accountHandle, String callingPackage) {
        ...
        return getTelephonyManager().getVoiceMailNumber(subId);
        ...
    }

    private TelephonyManager getTelephonyManager() {
        return (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
    }

 

1.2 PhoneSubInfoController— “iphonesubinfo” Service(PhoneSubInfoController中注冊)


服務端PhoneSubInfoController繼承自IPhoneSubInfo.Stub

public class PhoneSubInfoController extends IPhoneSubInfo.Stub {...}

在創建Default Phone對象之后,ProxyController對象在PhoneFactory.java的makeDefaultPhone()中被初始化

    public static void makeDefaultPhone(Context context) {
        ...
        //先初始化ProxyController
        mProxyController = ProxyController.getInstance(context, sProxyPhones,
                        mUiccController, sCommandsInterfaces);
        ...
    }

   private ProxyController(Context context, PhoneProxy[] phoneProxy, UiccController uiccController,
            CommandsInterface[] ci) {
        ...
        //在ProxyController的構造方法中初始化了PhoneSubInfoController對象
        mPhoneSubInfoController = new PhoneSubInfoController(mContext, mPhones);
        ... 
    }

    public PhoneSubInfoController(Context context, Phone[] phone) {
        mPhone = phone;
        if (ServiceManager.getService("iphonesubinfo") == null) {
            //將PhoneSubInfoController實例注冊到ServiceManager中
            ServiceManager.addService("iphonesubinfo", this);
        }
        mContext = context;
        mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
    }

所以在TelephonyManager中可以通過ServiceManager得到”iphonesubinfo” Service

    private IPhoneSubInfo getSubscriberInfo() {
        // get it each time because that process crashes a lot
        return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
    }

通過”iphonesubinfo” Service可以得到software version、deviceID、VoiceMail Number等信息,TelephonyManager在這里只是對這些方法進一步封裝,這些方法具體的實現,最后還是通過Phone實例和IsimRecords實例來完成的。
以getMsisdn()方法為例,最常見的調用方式如下:

PhoneInterfaceManager.init

備注:在Android N中已刪除PhoneSubInfo.java和PhoneSubInfoProxy.java,所以流程變得簡單了。

 

1.3 PhoneInterfaceManager—Telephony Service(PhoneInterfaceManager中注冊)


TelephonyManager依賴Telephony Service實現了大部分的方法。
PhoneInterfaceManager繼承自ITelephony.Stub

public class PhoneInterfaceManager extends ITelephony.Stub {

PhoneInterfaceManager.java在 packages\services\telephony\src\com\android\phone目錄下,顯然它是運行在Phone進程中的。
在Phone進程啟動時,Default Phone對象創建完之后,PhoneInterfaceManager對象在/packages/services/Telephony/src/com/android/phone/PhoneGlobals.java的onCreate()中被初始化:

    public void onCreate() {
        ...
        phoneMgr = PhoneInterfaceManager.init(this, PhoneFactory.getDefaultPhone());
        ...
    }

    /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone) {
        synchronized (PhoneInterfaceManager.class) {
            if (sInstance == null) {
                //初始化PhoneInterfaceManager
                sInstance = new PhoneInterfaceManager(app, phone);
            } else {
                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
            }
            return sInstance;
        }
    }

 

在PhoneInterfaceManager的構造方法中:

    private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
        //得到一些關鍵類
        mApp = app;
        mPhone = phone;
        mCM = PhoneGlobals.getInstance().mCM;
        mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
        mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
        mMainThreadHandler = new MainThreadHandler();
        mTelephonySharedPreferences =
                PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
        mSubscriptionController = SubscriptionController.getInstance();

        publish();
    }

    private void publish() {
        //將PhoneInterfaceManager實例注冊到ServiceManager中
        ServiceManager.addService("phone", this);
    }

 

在PhoneInterfaceManager初始化的時候,把自己注冊成SystemServer,這樣客戶端(如TelephonyManager)則可以通過ServiceManager把它取出來。

    private ITelephony getITelephony() {
        //得到PhoneInterfaceManager的代理對象
        return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
    }

 

PhoneInterfaceManager中的方法,最后還是通過Phone實例來實現。
以isImsRegistered()方法為例,最常見的調用方式如下:

 

1.4 TelephonyRegistry—“telephony.registry” Service(SystemServer.java中注冊)


TelephonyRegistry繼承自ITelephonyRegistry.Stub

class TelephonyRegistry extends ITelephonyRegistry.Stub {

 

在SystemServer.java中,

    telephonyRegistry = new TelephonyRegistry(context);  
    //將TelephonyRegistry實例注冊到ServiceManager中
    ServiceManager.addService("telephony.registry", telephonyRegistry);  

 

所以在TelephonyManager中可以通過ServiceManager得到”telephony.registry” Service

    if (sRegistry == null) {
        sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                "telephony.registry"));
    }

TelephonyManager主要利用”telephony.registry” Service實現listen()方法,實現對Phone狀態的監聽的功能

    public void listen(PhoneStateListener listener, int events) {
        if (mContext == null) return;
        try {
            Boolean notifyNow = (getITelephony() != null);
            sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
                    listener.callback, events, notifyNow);
        } catch (RemoteException ex) {
            // system process dead
        } catch (NullPointerException ex) {
            // system process dead
        }
    }

 

關於TelephonyRegistry,后續的文章會詳細講,目前先不用太關注。

 

 

2. 如何得到TelephonyManager對象


1、 假如沒有Context,可以通過:

 

private static TelephonyManager sInstance = new TelephonyManager();  
public static TelephonyManager getDefault() {  
    return sInstance;  
} 

 

2、如果能得到Context對象,可以通過:

//注意,這是從SystemService中取
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
//或者
TelephonyManager mTelephonyManager = TelephonyManager.from(context);

 

3. 其他重要方法

TelephonyManager還提供了兩個其他比較重要的方法:

     /**
     * Gets the telephony property.
     *
     * @hide
     */
    public static String getTelephonyProperty(int phoneId, String property, String defaultVal) {
        String propVal = null;
        //根據key獲取到value
        String prop = SystemProperties.get(property);
        if ((prop != null) && (prop.length() > 0)) {
            //將value分割成字符串數組
            String values[] = prop.split(",");
            if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) {
        //取出phoneId對應的value propVal = values[phoneId];
            }
        }
        return propVal == null ? defaultVal : propVal;
    }

     /**
     * Sets the telephony property with the value specified.
     *
     * @hide
     */
    public static void setTelephonyProperty(int phoneId, String property, String value) {
        ...
    }

 

這樣子就可以實現對於同一個key,不同phoneId可以存儲不同的值。


————————————————
版權聲明:本文為CSDN博主「linyongan」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/linyongan/article/details/52104394


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM