以Android14.0源码讲解
ITelephony来自framework下的com.android.internal.telephony包下
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl
这个接口用于与Phone交互的界面,主要由TelephonyManager类使用,一些地方仍在直接使用它,如果可能的话,请清理它们,并使用TelephonyManager。意思就是不让你直接使用ITelephony 接口,要用里面的方法的话就使用TelephonyManager来获取。
/**
94 * Interface used to interact with the phone. Mostly this is used by the
95 * TelephonyManager class. A few places are still using this directly.
96 * Please clean them up if possible and use TelephonyManager instead.
97 *
98 * {@hide}
99 */
100 interface ITelephony {
里面包含了384个方法,我们找一些常见的方法来学习。
1.通话相关
/**/*拨一个号码。这不会打电话。它显示拨号程序屏幕。*/
108 @UnsupportedAppUsage
109 void dial(String number);
110
111 /**拨打指定号码。*/
116 @UnsupportedAppUsage
117 void call(String callingPackage, String number);
350
351 /**返回特定Subscription的Call State。*/
354 int getCallStateForSubscription(int subId, String callingPackage, String featureId);
之前Android9.0还能 endCall(),answerRingingCall(),isRinging(String callingPackage)等可以控制通话的这些方法已经被移除了,只能获取通话的相关状态了。
在 Android 14 中,ITelephony 接口中的一些与呼叫控制相关的方法,如 endCall() 和 answerRingingCall(),被移除的原因主要是为了增强用户隐私和安全性,并遵循更严格的权限管理政策。以下是移除这些方法背后的主要原因:
- 用户隐私和安全
Android 操作系统近年来越来越重视用户隐私和安全性。方法如 endCall() 和 answerRingingCall(),允许应用程序以编程方式挂断电话或接听来电,这涉及对电话呼叫进行直接控制。虽然这些方法过去在某些场景下(例如自动应答、自动挂断电话)很有用,但它们也存在潜在的隐私风险:
滥用风险:应用可以在用户不知情或未授权的情况下控制呼叫,导致隐私问题。例如,某些恶意应用可以滥用这些API来挂断重要的电话或在不需要用户参与的情况下接听电话。
侵犯用户选择:这些方法绕过了用户的决定,让应用能够控制电话呼叫流程,而不经过用户的同意或操作。
为了解决这些问题,Google 在 Android 14 中移除了这些容易被滥用的方法,进一步保护用户的隐私。
- 权限管理的演变
随着 Android 的发展,Google 越来越强调权限的精细化管理,以确保用户对应用行为的更大控制。过去,某些方法可能只需要少量的权限(例如 READ_PHONE_STATE),但实际上,它们赋予应用极高的权力来控制用户的电话。
为了解决这种过度授权的问题,Google 逐步将与电话呼叫相关的操作限制给了系统应用或经过特别授权的应用,而普通第三方应用程序则无法再获得这些权限。
- 推动使用更安全的API
Google 提供了更安全且受限的替代方案来处理呼叫控制功能。例如,通过官方的 TelecomManager API,应用程序可以注册为默认电话应用来控制电话呼叫。要成为默认电话应用,用户必须显式授予权限,这样能够更好地避免未经授权的操作。这种设计确保了只有用户明确选择的应用程序才能执行类似的操作,减少滥用的可能性。
例如:
TelecomManager API:可以用来控制电话呼叫,但仅限于注册为默认电话应用的程序,并且权限控制更严格。
TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
telecomManager.endCall(); // 需要应用是默认电话应用
CallScreeningService:用于筛选和监控来电,同样受限于特定的系统权限。
-
限制低层次的接口调用
ITelephony 是 Android 系统中的一个底层接口,通常不面向普通应用程序开发者,而是为系统应用或有特殊权限的应用设计的。过去,一些开发者通过反射等手段调用这些内部API,而这些API绕过了系统层级的限制和权限检查。在 Android 14 中,Google 通过移除这些方法,加强了对底层接口的封装,减少了未授权调用的风险。 -
提高系统稳定性
随着 Android 系统的演变,Google 也希望简化并优化底层系统接口,以提高系统的稳定性和一致性。减少对底层 ITelephony API 的依赖,可以避免未来版本中出现的不兼容性问题,并减少系统维护的复杂度。
2.网络模式相关
553 /*返回subId的数据网络类型*/
559 int getDataNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);561 /** 返回subId的语音网络类型*/
569 int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);902 /*将网络选择模式设置为自动。*/
905 void setNetworkSelectionModeAutomatic(int subId);//让radio连接到输入网络,并将选择模式更改为手动。boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operatorInfo, boolean persisSelection);//执行radio扫描并返回可用网络列表。也就是 获取搜网后的结果CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage,String callingFeatureId);//发起搜网请求
931 int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
932 in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
933 in String callingPackage, String callingFeatureId);//停止搜网
941 void stopNetworkScan(int subId, int scanId);
设置网络模式(即,4G/3G/2G, 3G/2G, 2G only等) setPreferredNetworkType(int subId, int networkType);方法也移除了,普通应用不再能够直接更改这些设置。目前,只有具有 系统级权限 或 运营商特权 的应用程序才可能具有修改网络模式的权限。
3.手机相关
/*getDeviceId(String callingPackage);方法已经废弃使用getDeviceIdWithFeature代替,
返回Phone的唯一设备ID,例如IMEI GSM 和 CDMA phone的MEID。如果设备ID不可用,则返回null。*/
1317 String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);//通过slot 获取IMEI
1328 String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);//返回给定插槽的MEID。
1356 String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId);// 获取设备软件版本号
1374 String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage,
1375 String callingFeatureId);
IMEI:用于 GSM 和 LTE 网络的 15 位数字唯一标识符,广泛应用于全球。
MEID:主要用于 CDMA 网络的 14 位十六进制标识符。常见于美国和一些其他地区的移动通信网络。CDMA 网络由运营商如 Verizon(美国)或 KDDI(日本)使用。
在现代智能手机上,特别是双模设备(支持 GSM 和 CDMA 网络)的手机,可能同时有 IMEI 和 MEID 号码,分别用于不同的网络协议。随着 LTE 网络的发展,CDMA 技术的使用逐渐减少,因此 IMEI 逐渐成为全球统一的设备标识方法。
4.SIM卡相关
//提供一个pin来解锁特定subId的SIM卡。
163 boolean supplyPinForSubscriber(int subId, String pin);//提供puk以解锁SIM卡,并将SIM卡pin设置为新pin。
173 boolean supplyPukForSubscriber(int subId, String puk, String pin);// 判断是否插入了Sim卡
575 @UnsupportedAppUsage
576 boolean hasIccCard();//获取默认eUICC卡的卡ID。如果没有eUICC,则返回{@link#INVALID_CARD_ID}。
1732 int getCardIdForDefaultEuicc(int subId, String callingPackage);
接下来看一下这些方法是如何实现的?
查看源码,PhoneInterfaceManager类继承了接口ITelephony.Stub ,相当于实现了ITelephony的所有方法。
先了解一下AIDL机制:
那为什么源码中PhoneInterfaceManager 可以 extends ITelephony.Stub 接口?在 Java 和 Android 中,PhoneInterfaceManager 类通过 extends 继承 ITelephony.Stub 的情况,其实并不是简单的继承接口。这里涉及到了AIDL(Android Interface Definition Language)的特殊机制。1. AIDL(Android Interface Definition Language)
在 Android 中,AIDL 是一种允许不同进程之间通信的机制,常用于实现跨进程调用(IPC)。当您定义一个 AIDL 接口时,系统会自动为您生成一个 Stub 类。这些生成的代码通常会继承 android.os.Binder 类,并实现您定义的接口方法。例如,假设 ITelephony.aidl 是一个定义了远程接口的 AIDL 文件:interface ITelephony {void setPreferredNetworkType(int type);
}
在编译时,Android SDK 会自动生成一个 ITelephony.Stub 类,这个类是 Binder 的子类,并且它实现了 ITelephony 接口。Stub 类有助于在服务器端处理来自客户端的远程方法调用。2. ITelephony.Stub 是什么?
ITelephony.Stub 是 Android 自动生成的一个 抽象类,它既实现了 ITelephony 接口的所有抽象方法,又继承了 Binder 类。ITelephony 接口:定义了远程接口的行为(如 setPreferredNetworkType())。
ITelephony.Stub 类:这个抽象类实现了 IBinder 接口,并负责处理客户端的远程调用请求。它还提供了方法用于将远程请求分发给实际的 PhoneInterfaceManager 实现。
简而言之,Stub 类是 Binder 的子类,封装了 AIDL 中的绑定逻辑,用来处理跨进程调用。3. 为什么 PhoneInterfaceManager 可以 extends ITelephony.Stub?
由于 ITelephony.Stub 是一个抽象类,而不是一个接口,Java 中允许类继承另一个类。因此,PhoneInterfaceManager 继承 ITelephony.Stub 是合理的:java
public class PhoneInterfaceManager extends ITelephony.Stub {@Overridepublic void setPreferredNetworkType(int type) {// 具体的实现逻辑}
}
4. ITelephony.Stub 和 IPC 通信
PhoneInterfaceManager 继承 ITelephony.Stub 是为了处理来自其他进程的远程调用。由于 Android 系统中的很多服务(如电话、消息等)运行在单独的进程中,它们需要通过 AIDL 机制提供远程调用接口给其他进程使用。通过继承 ITelephony.Stub,PhoneInterfaceManager 类能够作为服务端,处理来自客户端(如应用层)发起的远程调用请求。5. 总结
PhoneInterfaceManager 继承 ITelephony.Stub,是因为 Stub 实际上是一个 AIDL 自动生成的 抽象类,它既实现了接口,又继承了 Binder 类,负责管理远程调用。
AIDL 机制允许跨进程调用,Stub 类帮助处理这些调用,因此 PhoneInterfaceManager 继承了 Stub,从而可以提供远程方法调用的服务。
这不是普通的类继承接口的情况,而是 AIDL 机制下的 自动生成类的继承。
看完小插曲,回到PhoneInterfaceManager类的学习,通过查看这个所在路径packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java,发现它是运行在phone进程下的,它是PhoneGlobals在onCreate方法中初始化的 phoneMgr = PhoneInterfaceManager.init(this),并且这个只能初始化一次。
2439 /**
2440 * Initialize the singleton PhoneInterfaceManager instance.
2441 * This is only done once, at startup, from PhoneApp.onCreate().
2442 */
2443 /* package */ static PhoneInterfaceManager init(PhoneGlobals app) {
2444 synchronized (PhoneInterfaceManager.class) {
2445 if (sInstance == null) {
2446 sInstance = new PhoneInterfaceManager(app);
2447 } else {
2448 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
2449 }
2450 return sInstance;
2451 }
2452 }
2453
2454 /** Private constructor; @see init() */
2455 private PhoneInterfaceManager(PhoneGlobals app) {
2456 mApp = app; //得到PhoneGlobals的引用
2457 mCM = PhoneGlobals.getInstance().mCM; //CallManager引用
2458 mImsResolver = ImsResolver.getInstance(); //获取ImsResolver实例对象
2459 mSatelliteController = SatelliteController.getInstance();//获取SatelliteController实例对象
2460 mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
2461 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
2462 mMainThreadHandler = new MainThreadHandler(); //获取MainThreadHandler对象
2463 mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp);
2464 mNetworkScanRequestTracker = new NetworkScanRequestTracker();
2465 mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
2466 mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
2467 mNotifyUserActivity = new AtomicBoolean(false);
2468 PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
2469 publish();
2470 CarrierAllowListInfo.loadInstance(mApp);
2471 }7 private void publish() {
2488 if (DBG) log("publish: " + this);
2489 //将当前对象注册为 Telephony 服务,使其可以被系统访问和使用。ServiceManager.addService("phone", this);
2490 TelephonyFrameworkInitializer
2491 .getTelephonyServiceManager()
2492 .getTelephonyServiceRegisterer()
2493 .register(this);
2494 }
先看getCallStateForSubscription方法是如何实现的?
3219 @Override
3220 public int getCallStateForSubscription(int subId, String callingPackage, String featureId) {//判断权限
3221 if (CompatChanges.isChangeEnabled(
3222 TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
3223 Binder.getCallingUid())) {
3224 // Check READ_PHONE_STATE for API version 31+
3225 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
3226 featureId, "getCallStateForSubscription")) {
3227 throw new SecurityException("getCallState requires READ_PHONE_STATE for apps "
3228 + "targeting API level 31+.");
3229 }
3230 }
3231 final long identity = Binder.clearCallingIdentity();
3232 try {
3233 Phone phone = getPhone(subId); //返回CALL_STATE状态
3234 return phone == null ? TelephonyManager.CALL_STATE_IDLE :
3235 PhoneConstantConversions.convertCallState(phone.getState());
3236 } finally {
3237 Binder.restoreCallingIdentity(identity);
3238 }
3239 }
把TelephonyManager.CALL_STATE_*转成PhoneConstants.State
frameworks/base/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java39 /**
40 * Convert the TelephonyManager.CALL_STATE_* constants into the
41 * {@link PhoneConstants.State} enum for the public API.
42 */
43 public static PhoneConstants.State convertCallState(int state) {
44 switch (state) {
45 case TelephonyManager.CALL_STATE_RINGING:
46 return PhoneConstants.State.RINGING;
47 case TelephonyManager.CALL_STATE_OFFHOOK:
48 return PhoneConstants.State.OFFHOOK;
49 default:
50 return PhoneConstants.State.IDLE;
51 }
52 }
53
再来看一个getDataNetworkTypeForSubscriber方法
5361 @Override
5362 public int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
5363 String callingFeatureId) {
5364 String functionName = "getDataNetworkTypeForSubscriber";
5365 if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
5366 mApp, functionName)) {
5367 if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
5368 mApp, subId, callingPackage, callingFeatureId, functionName)) {
5369 return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5370 }
5371 }
5372
5373 final long identity = Binder.clearCallingIdentity();
5374 try {
5375 final Phone phone = getPhone(subId);//也是通过phone对象来获取DataNetworkType
5376 if (phone != null) {
5377 return phone.getServiceState().getDataNetworkType();
5378 } else {
5379 return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5380 }
5381 } finally {
5382 Binder.restoreCallingIdentity(identity);
5383 }
5384 }
看了两个方法,可以发现在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。
既然PhoneInterfaceManager类是作为服务端的,那么客户端是怎么来获取这里面的方法呢,App的开发过程中是没办法获取到ITelephony接口的。可以通过 frameworks/base/telephony/java/android/telephony/TelephonyManager.java类来获取响应的方法,在TelephonyManager类中,ITelephony通过ITelephony.Stub.asInterface获取ITelephony类。
然后在telephony对象中获取getDataNetworkTypeForSubscriber方法,相当于远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法
6 public int getDataNetworkType(int subId) {
3147 try{//调用getITelephony()方法得到ITelephony的实例对象
3148 ITelephony telephony = getITelephony();
3149 if (telephony != null) {// 调用ITelephony实例对象的getDataNetworkTypeForSubscriber()方法// 实际上是远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法
3150 return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
3151 getAttributionTag());
3152 } else {
3153 // This can happen when the ITelephony interface is not up yet.
3154 return NETWORK_TYPE_UNKNOWN;
3155 }
3156 } catch(RemoteException ex) {
3157 // This shouldn't happen in the normal case
3158 return NETWORK_TYPE_UNKNOWN;
3159 } catch (NullPointerException ex) {
3160 // This could happen before phone restarts due to crashing
3161 return NETWORK_TYPE_UNKNOWN;
3162 }
3163 }
至此就完成了,但是getDataNetworkTypeForSubscriber方法中返回的这个数据phone.getServiceState().getDataNetworkType(),其实要深究的话,就要到ServiceState类了
/frameworks/base/telephony/java/android/telephony/ServiceState.java1770 public @NetworkType int getDataNetworkType() {
1771 final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
1772 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
1773 final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
1774 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1775
1776 // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
1777 // the RAT from cellular.
1778 if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
1779 return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
1780 : TelephonyManager.NETWORK_TYPE_UNKNOWN;
1781 }
1782
1783 // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
1784 // use the RAT from IWLAN service is cellular is out of service, or when both are in service
1785 // and any APN type of data is preferred on IWLAN.
1786 if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
1787 return iwlanRegInfo.getAccessNetworkTechnology();
1788 }
1789
1790 // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
1791 // the RAT from cellular.
1792 return wwanRegInfo.getAccessNetworkTechnology();
1793 }
它们的调用关系:
1> App获取TelephonyManger的实例对象
2>然后调用TelephonyManger的getDataNetworkType()
3>在方法中通过getITelephony()获取ITelephony 实例
4>ITelephony 跨进程 调用PhoneInterfaceManager类中的getDataNetworkTypeForSubscriber
5>而getDataNetworkTypeForSubscriber方法中又通过phone对象调用ServiceState返回DataNetworkType