• Android Phone设计介绍


    Android Phone设计介绍

    Android之rild进程启动源码分析一文中已经详细介绍了Android的电话系统架构设计,并对rild进程进行了详细的剖析。native层的rild进程负责与底层modem设备交互,比如向modem发送AT命令、从modem中接收消息,同时实时监控modem的状态;作为三层架构设计的Android电话系统:客户端的framework、服务端的rild、modem设备,rild进程还需要接收来自客户端的命令消息,将客户端的需求转发给modem设备,实现对modem的控制。

    Framework的RIL与Native的Rild交互框图:

    Android中关于telephony相关的java代码主要在下列目录中:
    1. frameworks/base/telephony/java/android/telephony
       提供Android telephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager。
    2. frameworks/base/telephony/java/com/android/internal/telephony
    3. frameworks/base/services/java/com/android/server/TelephonyRegistry.java
       提供一系列内部接口,目前第三方应用还不能使用。当前似乎只有packages/apps/Phone能够使用
    4. packages/apps/Phone
       一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。

    TelephonyManager主要使用两个服务来访问telephony功能:
    1. ITelephony,提供与telephony进行操作,交互的接口

        在packages/apps/Phone中由PhoneInterfaceManager.java实现。
    2. ITelephonyRegistry, 提供登记telephony事件的接口

        在frameworks/base/services/java/com/android/server/TelephonyRegistry.java实现。

    在framework层提供并实现了一套完整的电话操作接口,这些接口描述了电话的各个功能模块,相关类关系图如下:



    BaseCommands实现了CommandsInterface的部分接口,用于通知手机各种内部状态的变化。它里面保存了大量的Registrant(注册者)和RegistrantList,并提供了大量的registerXXX和unregisterXXX函数。当对某种状态变化感兴趣时,就可以调用registerXXX函数,在BaseCommands内部创建一个对应的registrant或将registrant添加到列表中。后者表示某种状态有多个感兴趣者,将各个registrant添加到RegistrantList中,若只能有一个感兴趣者则创建的是Registrant对象。实际上,在调用registerXXX函数时,调用者将message和handler传递给Registrant对象;当有状态变化时则通过Registrant.NotifyXXX调用Handler将消息发送到handler的消息队列上,调用者可以在一个线程中去处理这些消息,因此实现了尽可能实时通知调用者的目的。RIL类负责与rild交互,提供API用于执行AT命令,即RIL请求,供调用者调用;以及处理上报的回复,以消息的形式发送给调用者。

    CommandsInterface 描述了对电话的所有操作接口,如命令,查询状态,以及电话事件监听等,BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。RIL类继承自BaseCommands,实现了CommandsInterface的部分接口,包含了2个线程sender和receiver(在RIL的构造函数中创建并启动运行),前者用于向rild发送RIL At命令请求,后者用于处理来自rild的response信息(包括对RIL请求的回复和Modem主动上报的unsolicited信息)。它们都是向本地socket读写数据来实现的。使用者在使用RIL请求API时,通过sender线程向rild发送RIL请求,具体是:在这些API函数中,得到一个RILRequest后,将要传递给AT命令的数据通过Parcel写入到socket中,rild侧解析得到传递来的数据,根据请求号调用相应的分发函数转换成AT命令传递给Modem。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,通过线程RILSender发送给rild。线程RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种:

    1. 电话事件,RIL_UNSOL_xxx, RIL读取相应数据后,发送message给对应的handler 。
    2. 命令的异步响应。
     
    Android电话描述相关类关系图如下:

    Phone描述了对电话的所有操作接口。PhoneBase直接从Phone 派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA 和GSM的操作。PhoneProxy也从Phone直接派生而来。当前不需要区分具体是CDMA Phone还是GSM Phone时,可使用PhoneProxy。GSMPhone和CDMAPhone实现了Phone中定义的接口。接口类Phone定义了一套API,这套API用于使用RIL发送AT命令请求,也还有一套register和unregister函数;当调用者对一些内部状态感兴趣时,可以调用对应的register函数,当状态变化时可以得到及时通知。PhoneBase实现了Phone接口中定义的部分函数,还有一部分由其子类GSMPhone和CDMAPhone实现。PhoneProxy是GSMPhone和CDMAPhone的代理,让使用者不用关注手机到底是GSM还是CDMA,它遵守Phone定义的API接口,因此继承Phone。PhoneFactory在创建Phone对象时,拥有的是PhoneProxy对象,PhoneProxy根据实际的网络类型创建对应的GSMPhone或CDMAPhone。PhoneFactory同样拥有CommandInterface的接口对象,即RIL的实例,该RIL实例将被传递给GSMPhone或CDMAPhone,即GSMPhone或CDMAPhone引用它,实现与rild的交互。GSMPhone和CDMAPhone继承自PhoneBase,它们(包括PhoneProxy)都是一个Handler。这样,它们就可以在线程的循环Looper.loop中处理来自RILJ或自身发送的各种Message。PhoneFactory负责创建各个Phone实例,其成员及成员函数为static,可以保证创建的实例在系统运行时的唯一性。

    电话tracker类关系图:

     Phone进程启动

    Phone就象个后台进程一样,开机即运行并一直存在,当有来电时,它会作出反应,如显示UI和铃声提示;当在通话过程中,它显示InCallScreen;当要拨号时ITeleohony的接口调用最终到Phone进程,然后由它去与PhoneFactory创建的GSMPhone或CDMAPhone进行交互;通话记录也是由Phone添加到数据库里的。

    Android的Phone进程并不是在点击Luncher上的图标启动的,而是在系统开机启动时,又ActivityManagerService启动的。Phone的源码位于packagesappsPhone。在Phone的AndroidManifest.xml文件配置了如下属性:

    [html] view plaincopy
     
    1. <application android:name="PhoneApp"  
    2.                  android:persistent="true"  
    3.                  android:label="@string/dialerIconLabel"  
    4.                  android:icon="@drawable/ic_launcher_phone">  
    [html] view plaincopy
     
    1. <application android:name="PhoneApp"  
    2.                  android:persistent="true"  
    3.                  android:label="@string/dialerIconLabel"  
    4.                  android:icon="@drawable/ic_launcher_phone">  

    对于android:persistent="true" 的应用是在Android开机时启动的,在Android服务启动中,有三种启动方式,其中一种是在启动完成时通过调用systemReady函数来完成并通知服务启动。ActivityManagerService服务正是采用这种启动方式:

    [java] view plaincopy
     
    1. public void systemReady(final Runnable goingCallback){  
    2.     ......  
    3.     if (goingCallback != null) goingCallback.run();  
    4.     synchronized (this) {  
    5.         if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
    6.             try {  
    7.                 //通过PackageManager查询到所有android:persistent属性为true的应用   
    8.                 List apps = AppGlobals.getPackageManager().getPersistentApplications(STOCK_PM_FLAGS);  
    9.                 if (apps != null) {  
    10.                     int N = apps.size();  
    11.                     int i;  
    12.                     //遍历所有应用,并启动   
    13.                     for (i=0; i<N; i++) {  
    14.                         //得到每个应用的相关信息   
    15.                         ApplicationInfo info= (ApplicationInfo)apps.get(i);  
    16.                         //启动应用   
    17.                         if (info != null &&!info.packageName.equals("android")) {  
    18.                             addAppLocked(info);  
    19.                         }  
    20.                     }  
    21.                 }  
    22.             } catch (RemoteException ex) {  
    23.                 // pm is in same process, this will never happen.   
    24.             }  
    25.         }  
    26.         ........  
    27.         mMainStack.resumeTopActivityLocked(null);  
    28.     }  
    29. }  
    [java] view plaincopy
     
    1. public void systemReady(final Runnable goingCallback){  
    2.     ......  
    3.     if (goingCallback != null) goingCallback.run();  
    4.     synchronized (this) {  
    5.         if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
    6.             try {  
    7.                 //通过PackageManager查询到所有android:persistent属性为true的应用  
    8.                 List apps = AppGlobals.getPackageManager().getPersistentApplications(STOCK_PM_FLAGS);  
    9.                 if (apps != null) {  
    10.                     int N = apps.size();  
    11.                     int i;  
    12.                     //遍历所有应用,并启动  
    13.                     for (i=0; i<N; i++) {  
    14.                         //得到每个应用的相关信息  
    15.                         ApplicationInfo info= (ApplicationInfo)apps.get(i);  
    16.                         //启动应用  
    17.                         if (info != null &&!info.packageName.equals("android")) {  
    18.                             addAppLocked(info);  
    19.                         }  
    20.                     }  
    21.                 }  
    22.             } catch (RemoteException ex) {  
    23.                 // pm is in same process, this will never happen.  
    24.             }  
    25.         }  
    26.         ........  
    27.         mMainStack.resumeTopActivityLocked(null);  
    28.     }  
    29. }  

    在该函数中,首先查询android:persistent="true"的应用,然后调用addAppLocked启动该应用

    [java] view plaincopy
     
    1. final ProcessRecord addAppLocked(ApplicationInfo info) {  
    2.     //根据进程名称及uid查询相应的进程   
    3.     ProcessRecord app = getProcessRecordLocked(info.processName, info.uid);  
    4.     //如果系统中不存在该进城,则创建一个新的进程   
    5.     if (app == null) {  
    6.         app = newProcessRecordLocked(null, info, null);  
    7.         mProcessNames.put(info.processName, info.uid, app);  
    8.         updateLruProcessLocked(app, true, true);  
    9.     }  
    10.     // This package really, really can not be stopped.   
    11.     try {  
    12.         AppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false);  
    13.     } catch (RemoteException e) {  
    14.     } catch (IllegalArgumentException e) {  
    15.         Slog.w(TAG, "Failed trying to unstop package "+ info.packageName + ": " + e);  
    16.     }  
    17.   
    18.     if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))  
    19.             == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {  
    20.         app.persistent = true;  
    21.         app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;  
    22.     }  
    23.     if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {  
    24.         mPersistentStartingProcesses.add(app);  
    25.         //启动该应用进程   
    26.         startProcessLocked(app, "added application", app.processName);  
    27.     }  
    28.     return app;  
    29. }  
    [java] view plaincopy
     
    1. final ProcessRecord addAppLocked(ApplicationInfo info) {  
    2.     //根据进程名称及uid查询相应的进程  
    3.     ProcessRecord app = getProcessRecordLocked(info.processName, info.uid);  
    4.     //如果系统中不存在该进城,则创建一个新的进程  
    5.     if (app == null) {  
    6.         app = newProcessRecordLocked(null, info, null);  
    7.         mProcessNames.put(info.processName, info.uid, app);  
    8.         updateLruProcessLocked(app, true, true);  
    9.     }  
    10.     // This package really, really can not be stopped.  
    11.     try {  
    12.         AppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false);  
    13.     } catch (RemoteException e) {  
    14.     } catch (IllegalArgumentException e) {  
    15.         Slog.w(TAG, "Failed trying to unstop package "+ info.packageName + ": " + e);  
    16.     }  
    17.   
    18.     if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))  
    19.             == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {  
    20.         app.persistent = true;  
    21.         app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;  
    22.     }  
    23.     if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {  
    24.         mPersistentStartingProcesses.add(app);  
    25.         //启动该应用进程  
    26.         startProcessLocked(app, "added application", app.processName);  
    27.     }  
    28.     return app;  
    29. }  

    应用启动核心函数是startProcessLocked,该函数已经在Zygote孵化应用进程过程的源码分析中详细介绍了。由此可知对于属性android:persistent为true的应用是在ActivityManagerService服务启动完成后启动的。PhoneAPP继承自Application,启动时调用它的onCreate函数:

    [java] view plaincopy
     
    1. public void onCreate() {  
    2.     if (VDBG) Log.v(LOG_TAG, "onCreate()...");  
    3.   
    4.     ContentResolver resolver = getContentResolver();  
    5.     Settings.Secure.putInt(resolver, Settings.Secure.RADIO_OPERATION, 0);  
    6.     sVoiceCapable =getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);  
    7.     if (phone == null) {  
    8.         // Get the default phone   
    9.         phone = PhoneFactory.makeDefaultPhone(this);  
    10.   
    11.         // Start TelephonyDebugService After the default phone is created.   
    12.         Intent intent = new Intent(this, TelephonyDebugService.class);  
    13.         startService(intent);  
    14.   
    15.         mCM = CallManager.getInstance();  
    16.         mCM.registerPhone(phone);  
    17.   
    18.         // Create the NotificationMgr singleton, which is used to display   
    19.         // status bar icons and control other status bar behavior.   
    20.         notificationMgr = NotificationMgr.init(this);  
    21.   
    22.         phoneMgr = PhoneInterfaceManager.init(this, phone);  
    23.   
    24.         mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);  
    25.   
    26.         int phoneType = phone.getPhoneType();  
    27.   
    28.         if (phoneType == Phone.PHONE_TYPE_CDMA) {  
    29.                     ...  
    30.         }  
    31.   
    32.         if (BluetoothAdapter.getDefaultAdapter() != null) {  
    33.             mBtHandsfree = BluetoothHandsfree.init(this, mCM);  
    34.             startService(new Intent(this, BluetoothHeadsetService.class));  
    35.         } else {  
    36.             // Device is not bluetooth capable   
    37.             mBtHandsfree = null;  
    38.         }  
    39.         ringer = Ringer.init(this);  
    40.         mReceiver = new PhoneAppBroadcastReceiver();  
    41.         mMediaButtonReceiver = new MediaButtonBroadcastReceiver();  
    42.         // before registering for phone state changes   
    43.         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  
    44.         ...  
    45.         // create mAccelerometerListener only if we are using the proximity sensor   
    46.         if (proximitySensorModeEnabled()) {  
    47.             mAccelerometerListener = new AccelerometerListener(this, this);  
    48.         }  
    49.         mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);  
    50.         mPowerManagerService = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));  
    51.         callController = CallController.init(this);  
    52.         inCallUiState = InCallUiState.init(this);  
    53.         callerInfoCache = CallerInfoCache.init(this);  
    54.         notifier = CallNotifier.init(this, phone, ringer, mBtHandsfree, new CallLogAsync());  
    55.         // register for ICC status   
    56.         IccCard sim = phone.getIccCard();  
    57.         if (sim != null) {  
    58.             if (VDBG) Log.v(LOG_TAG, "register for ICC status");  
    59.             sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);  
    60.         }  
    61.   
    62.         // register for MMI/USSD   
    63.         mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);  
    64.   
    65.         // register connection tracking to PhoneUtils   
    66.         PhoneUtils.initializeConnectionHandler(mCM);  
    67.   
    68.         // Read platform settings for TTY feature   
    69.         mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled);  
    70.   
    71.         // Register for misc other intent broadcasts.   
    72.         IntentFilter intentFilter =new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);  
    73.         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);  
    74.         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);  
    75.         intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);  
    76.         intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);  
    77.         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);  
    78.         intentFilter.addAction(Intent.ACTION_LOCALE_CHANGED);  
    79.         intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);  
    80.         intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);  
    81.         intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);  
    82.         intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);  
    83.         if (mTtyEnabled) {  
    84.             intentFilter.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);  
    85.         }  
    86.         intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);  
    87.         registerReceiver(mReceiver, intentFilter);  
    88.         ...  
    89.         AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);  
    90.           
    91.         PhoneUtils.setAudioMode(mCM);  
    92.     }  
    93.         ...  
    94.     inflateView();  
    95.     Log.d(LOG_TAG,"Send Broadcast ACTION_PHONE_RESTART");  
    96.     Intent intent = new Intent(TelephonyIntents.ACTION_PHONE_RESTART);  
    97.     sendBroadcast(intent);  
    98. }  
    [java] view plaincopy
     
    1. public void onCreate() {  
    2.     if (VDBG) Log.v(LOG_TAG, "onCreate()...");  
    3.   
    4.     ContentResolver resolver = getContentResolver();  
    5.     Settings.Secure.putInt(resolver, Settings.Secure.RADIO_OPERATION, 0);  
    6.     sVoiceCapable =getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);  
    7.     if (phone == null) {  
    8.         // Get the default phone  
    9.         phone = PhoneFactory.makeDefaultPhone(this);  
    10.   
    11.         // Start TelephonyDebugService After the default phone is created.  
    12.         Intent intent = new Intent(this, TelephonyDebugService.class);  
    13.         startService(intent);  
    14.   
    15.         mCM = CallManager.getInstance();  
    16.         mCM.registerPhone(phone);  
    17.   
    18.         // Create the NotificationMgr singleton, which is used to display  
    19.         // status bar icons and control other status bar behavior.  
    20.         notificationMgr = NotificationMgr.init(this);  
    21.   
    22.         phoneMgr = PhoneInterfaceManager.init(this, phone);  
    23.   
    24.         mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);  
    25.   
    26.         int phoneType = phone.getPhoneType();  
    27.   
    28.         if (phoneType == Phone.PHONE_TYPE_CDMA) {  
    29.                     ...  
    30.         }  
    31.   
    32.         if (BluetoothAdapter.getDefaultAdapter() != null) {  
    33.             mBtHandsfree = BluetoothHandsfree.init(this, mCM);  
    34.             startService(new Intent(this, BluetoothHeadsetService.class));  
    35.         } else {  
    36.             // Device is not bluetooth capable  
    37.             mBtHandsfree = null;  
    38.         }  
    39.         ringer = Ringer.init(this);  
    40.         mReceiver = new PhoneAppBroadcastReceiver();  
    41.         mMediaButtonReceiver = new MediaButtonBroadcastReceiver();  
    42.         // before registering for phone state changes  
    43.         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);  
    44.         ...  
    45.         // create mAccelerometerListener only if we are using the proximity sensor  
    46.         if (proximitySensorModeEnabled()) {  
    47.             mAccelerometerListener = new AccelerometerListener(this, this);  
    48.         }  
    49.         mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);  
    50.         mPowerManagerService = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));  
    51.         callController = CallController.init(this);  
    52.         inCallUiState = InCallUiState.init(this);  
    53.         callerInfoCache = CallerInfoCache.init(this);  
    54.         notifier = CallNotifier.init(this, phone, ringer, mBtHandsfree, new CallLogAsync());  
    55.         // register for ICC status  
    56.         IccCard sim = phone.getIccCard();  
    57.         if (sim != null) {  
    58.             if (VDBG) Log.v(LOG_TAG, "register for ICC status");  
    59.             sim.registerForNetworkLocked(mHandler, EVENT_SIM_NETWORK_LOCKED, null);  
    60.         }  
    61.   
    62.         // register for MMI/USSD  
    63.         mCM.registerForMmiComplete(mHandler, MMI_COMPLETE, null);  
    64.   
    65.         // register connection tracking to PhoneUtils  
    66.         PhoneUtils.initializeConnectionHandler(mCM);  
    67.   
    68.         // Read platform settings for TTY feature  
    69.         mTtyEnabled = getResources().getBoolean(R.bool.tty_enabled);  
    70.   
    71.         // Register for misc other intent broadcasts.  
    72.         IntentFilter intentFilter =new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);  
    73.         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);  
    74.         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);  
    75.         intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);  
    76.         intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);  
    77.         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);  
    78.         intentFilter.addAction(Intent.ACTION_LOCALE_CHANGED);  
    79.         intentFilter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);  
    80.         intentFilter.addAction(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);  
    81.         intentFilter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);  
    82.         intentFilter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);  
    83.         if (mTtyEnabled) {  
    84.             intentFilter.addAction(TtyIntent.TTY_PREFERRED_MODE_CHANGE_ACTION);  
    85.         }  
    86.         intentFilter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);  
    87.         registerReceiver(mReceiver, intentFilter);  
    88.         ...  
    89.         AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);  
    90.           
    91.         PhoneUtils.setAudioMode(mCM);  
    92.     }  
    93.         ...  
    94.     inflateView();  
    95.     Log.d(LOG_TAG,"Send Broadcast ACTION_PHONE_RESTART");  
    96.     Intent intent = new Intent(TelephonyIntents.ACTION_PHONE_RESTART);  
    97.     sendBroadcast(intent);  
    98. }  

    CallNotifier是一个Handler,它为PhoneApp处理各个主动上报来的一些消息。它监听来自Telephony层phone状态变化和其它各种事件,从而作出反应 如各种UI行为:启动铃音播放和来电显示UI、播放正在通话时的来电提示、更新状态栏提示(通过NotificationMgr)、通话记录添加等。在PhoneBase中提供了一些RegistrantList,CallNotifier可以将自己作为一个感兴趣者注册进去,这样,当状态变化时,CallNotifier将得到通知,然后在线程中对其处理,作出UI方面的响应。

    NotificationMgr以静态成员函数的方式为PhoneApp用于Phone进程在状态栏中通知用户消息的功能,诸如:有未接电话、正在通话、是否静音等信息。它使用系统提供的API类NotificationManager和StatusBarManager完成通知功能。每项通知对应着通知、更新通知和取消通知的函数。当收到Message时,PhoneApp的Handler的handleMessage会使用NotificationMgr更新状态栏信息。下面的代码片段用于更新状态栏的的提示信息:

    phonesrccomandroidphonePhoneApp.java

    [java] view plaincopy
     
    1. case EVENT_UPDATE_INCALL_NOTIFICATION:  
    2.   notificationMgr.updateInCallNotification();//通话提示   
    3.   break;  
    4. case EVENT_DATA_ROAMING_DISCONNECTED:  
    5.   notificationMgr.showDataDisconnectedRoaming();//因漫游数据连接断开提示   
    6.   break;  
    7. case EVENT_DATA_ROAMING_OK:  
    8.   notificationMgr.hideDataDisconnectedRoaming();//隐藏漫游断开提示   
    9.   break;  
    [java] view plaincopy
     
    1. case EVENT_UPDATE_INCALL_NOTIFICATION:  
    2.   notificationMgr.updateInCallNotification();//通话提示  
    3.   break;  
    4. case EVENT_DATA_ROAMING_DISCONNECTED:  
    5.   notificationMgr.showDataDisconnectedRoaming();//因漫游数据连接断开提示  
    6.   break;  
    7. case EVENT_DATA_ROAMING_OK:  
    8.   notificationMgr.hideDataDisconnectedRoaming();//隐藏漫游断开提示  
    9.   break;  

    是否有未接电话的提示则是在PhoneApp创建NotificationMgr对象并调用其初始化函数时检查提示的。


    InCallScreen它是手机正在通话时的Activity。当有来电、开始拨号或正在通话时,运行的是该Activity,InCallScreen需要处理来电时跳过键盘锁直接可以接听电话、是否有耳机插入的情况、是否用蓝牙接听电话、需要监听并维护更新通话状态并显示给用户、需要支持通话过程中的某些功能(如发送DTMF、电话会议、分离一路通话)操作、OTA Call等。CallCard是InCallScreen中的一个call(可能是当前的Call或保持的Call或来电Call)。当需要接听电话或拨打电话时,上层发来intent,然后InCallScreen收到intent时它的InCallScreen.onNewIntent函数被调用,解析intent,要么调用placeCall拨打电话,要么调用internalAnswerCall接听电话。

    InCallTouchUi:通话过程中的按钮功能以及来电接听时的滑动接听功能。
    ManageConferenceUtils:管理多方通话的工具,包括部分UI元素。借助PhoneUtils实现其功能。
    DTMFTwelveKeyDialer:通话状态时的拨号盘,用于发送DTMF。
    DTMFTwelveKeyDialerView:DTMF拨号视图布局类。
    InCallControlState:维护着一些状态信息,诸如是否Enable了Speaker声音免提、是否可以添加新的一路通话等等。它是MVC模式的数据部分。
    InCallMenu:通话状态菜单,里面包含各个菜单项

  • 相关阅读:
    【常用配置】Spring框架web.xml通用配置
    3.从AbstractQueuedSynchronizer(AQS)说起(2)——共享模式的锁获取与释放
    2.从AbstractQueuedSynchronizer(AQS)说起(1)——独占模式的锁获取与释放
    1.有关线程、并发的基本概念
    0.Java并发包系列开篇
    SpringMVC——DispatcherServlet的IoC容器(Web应用的IoC容器的子容器)创建过程
    关于String的问题
    Spring——Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)
    <<、>>、>>>移位操作
    System.arraycopy(src, srcPos, dest, destPos, length) 与 Arrays.copyOf(original, newLength)区别
  • 原文地址:https://www.cnblogs.com/kevincode/p/3837901.html
Copyright © 2020-2023  润新知