Android L 5.0 MT流程 代码

MT6750处理器资料介绍 MT6750处理器介绍:                                                                     MT6750单芯片是联发科技支持LTE Cat-6技术的中端产品,以八核心的强大运算能力执行新一代的调制解调技术,支持各式主流的IP多媒体子系统(IMS),包含 VoLTE、ViLTE、VoWiFi、RCS ,以优异性能提升界面显示效... 阅读详情
  1. --------
  2. 写在前:本人新手有理解不准确或者错误的地方请大家指正出来,我好及时修改
  3. -----
  4. ---我理解的来电流程大概分为6个部分--
  5. 注:1>2>3并不是只步骤,是指经过的第1>2>3个类。
① 1>2>3 RIL>GSMPhone      
    状态变化>发出来电通知
② 4>5>6>7>8>9 PstnIncomingCallNotifier>Call    
    接收到通知>准备创建连接
③ 10>11>12>13CreateConnectionProcesser> ConnectionServices 
    开始创建连接>创建连接完成。
④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager
    处理这个创建的 连接>成功来电
⑤ 8>15>16>17     CallsManager>Phone
    成功来电>准备启动界面
⑥ 18>19>20>21>22 CallList>StatubarNotifier
    开始启动界面 显示来电
----

framwork

①  RIL>GSMPhone    Call状态变化 -> 发出来电通知

  1. 0. Modem发出Call状态变化的通知,


  2. 1. framwork/opt/telephony/.../RIL.java
    1.1.RIL接收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息
  1. 1.2.然后经由mCallStateRegistrants.notifyRegistrants发出通知(RegistrantList消息处理机制此处不做具体说明)。
  2. 
            
    1. private void processUnsolicited (Parcel p) {
    2. ...
    3. switch(response) {
    4. ...
    5. case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
    6.                 if (RILJ_LOGD) unsljLog(response);
    7.                 mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
BaseCommands.java registerForCallStateChanged()  mCallStateRegistrants.add(r);
注册为观察者(android源码中大量用到观察者模式,或者叫RegistrantList消息处理机制)。

        
  1.  @Override
  2.     public void registerForCallStateChanged(Handler h, int what, Object obj) {
  3.         Registrant r = new Registrant (h, what, obj);
  4. //添加到观察者列表
  5.         mCallStateRegistrants.add(r);
  6.     }

2. framwork/opt/telephony/...GSMCallTracker.java
2.1查找察者被调用的地方(AS中的快捷键Ctrl+Alt+H), 两处被响应处理处理,其中一处:GSMCallTracker handleMessage

        
  1. ...//registerForCallStateChanged调用
  2. mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
  3. ...
  4. @Override
  5. public void
  6. //响应处理
  7. handleMessage (Message msg) {
  8. ...
  9. case EVENT_CALL_STATE_CHANGE:
  10. //调用父类CallTracker查询Call List方法
  11.      pollCallsWhenSafe();
  12. break;
2.1.1. pollCallsWhenSafe()方法在CallTracker.java中实现

        
  1. protected void pollCallsWhenSafe() {
  2. mNeedsPoll = true;

  3. if (checkNoOperationsPending()) {
  4. mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
  5. mCi.getCurrentCalls(mLastRelevantPoll);//RIL.java中的getCurrentCalls方法
  6. }
  7. }
2.1.2 回到RIL.java   getCurrentCalls  将 RIL_REQUEST_GET_CURRENT_CALLS 消息封装成 RILRequest 类型并发送。
此处 result为 EVENT_POLL_CALLS_RESULT,所以后 面 GSMCallTracker 中 hand lemessage响应的是这个消息。

         
  1. @Override
  2. public void
  3. getCurrentCalls (Message result) {
  4. RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);
  5. if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  6. send(rr);
  7. }
2.2 RIL.java 有三处接收处理 RIL_REQUEST_GET_CURRENT_CALLS 消息,真正的逻辑处理在processSolicited方法

         
  1. private RILRequest processSolicited (Parcel p) {
  2. ...
  3. case RIL_REQUEST_GET_CURRENT_CALLS: ret =  responseCallList(p); break;
  4. ...
  5. if (rr.mResult != null) {
  6.                     AsyncResult.forMessage(rr.mResult, null, tr);
  7.                     rr.mResult.sendToTarget();//发出handler消息通知
  8.                 }

2.3 回到framworks/opt/telephony/.../telephony/gsm/GSMCallTracker.java
rr . mResult . sendToTarget ()发出handler消息通知后, handleMessage方法中响应消息“ EVENT_POLL_CALLS_RESULT",这个消息是在  pollCallsWhenSafe中封装的。

         
  1. @Override
  2. public void
  3. handleMessage (Message msg) {
  4. ...
  5.  case EVENT_POLL_CALLS_RESULT:
  6.                 ar = (AsyncResult)msg.obj;
  7.                 if (msg == mLastRelevantPoll) {
  8.                     if (DBG_POLL) log(
  9.                             "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
  10.                     mNeedsPoll = false;
  11.                     mLastRelevantPoll = null;
  12.                     handlePollCalls((AsyncResult)msg.obj);
  13.                 }
  14.             break;
handlePollCalls方法根据RIL发出的Call List对象判断Call的状态,并发出不同的通知,
1) 新来电的通知是: phone. notifyNewRingingConnection; 

        
  1. handlePollCalls(){
  2. ...
  3. if (newRinging != null) {
  4. mPhone.notifyNewRingingConnection(newRinging);
  5. }
另外两个是 
2) 通话断开通知 onDisconnected;
3) Call状态变化通知 phone.notifiyPreciseCallStateChanged.
来电的时候发出的是phone.notifyNewRingConnection通知,进入到notifyNewRingConnection方法

3. framworks/opt/telephony/.../telephony/gsm/GSMPhone.java

       
  1.  public void notifyNewRingingConnection(Connection c) {
  2.         super.notifyNewRingingConnectionP(c);
  3.     }
调用父类   PhoneBase.java 
notifyNewRingingConnectionP() 发出来电通知 mNewRingingConnectionRegistrants.notifyRegistrants(ar);

        
  1. /**
  2.      * Notify registrants of a new ringing Connection.
  3.      * Subclasses of Phone probably want to replace this with a
  4.      * version scoped to their packages
  5.      */
  6.     public void notifyNewRingingConnectionP(Connection cn) {
  7.         if (!mIsVoiceCapable)
  8.             return;
  9.         AsyncResult ar = new AsyncResult(null, cn, null);
  10.         mNewRingingConnectionRegistrants.notifyRegistrants(ar);
  11.     }
RegistrantList.java

         
  1. public /*synchronized*/ void
  2. notifyRegistrants(AsyncResult ar)
  3. {
  4. internalNotifyRegistrants(ar.result, ar.exception);
  5. }

          
  1. private synchronized void
  2. internalNotifyRegistrants (Object result, Throwable exception)
  3. {
  4. for (int i = 0, s = registrants.size(); i < s ; i++) {
  5. Registrant r = (Registrant) registrants.get(i);
  6. r.internalNotifyRegistrant(result, exception);
  7. }
  8. }


         
  1. /*package*/ void
  2. internalNotifyRegistrant (Object result, Throwable exception)
  3. {
  4. Handler h = getHandler();
  5. if (h == null) {
  6. clear();
  7. } else {
  8. Message msg = Message.obtain();
  9. msg.what = what;
  10. msg.obj = new AsyncResult(userObj, result, exception);
  11. h.sendMessage(msg);
  12. }
  13. }
 
注册为观察者的方法为:

         
  1. // Inherited documentation suffices.
  2. @Override
  3. public void registerForNewRingingConnection(
  4. Handler h, int what, Object obj) {
  5. checkCorrectThread(h);

  6. mNewRingingConnectionRegistrants.addUnique(h, what, obj);
  7. }
registerForNewRingingConnection这个方法在4个地方被调用,即有4个地方关心是否有新来电的变化。
     1.packages/servicesTelephony/.../PstnIncomingCallNotifier.java   log:  D/Telephony( 1396): PstnIncomingCallNotifier: handleNewRingingConnection
     2.framworks/opt/telephony/.../PhoneProxy.java 
     3.framworks/opt/telephony/test/.../GSMPhoneTEST.java
     4.framworks/opt/telephony/.../telephony/CallManager.java 5.0之前是这里处理,新版本由PstnIncomingCallNotifier处理

----------------
前面:RIL发出Call状态变化消息通知,GSMPhone发出来电通知
----------------

Telephony

② PstnIncomingCallNotifier>Call  接收Framework层到通知>准备创建连接



4. packages/services/Telephony/.../PstnIncomingCallNotifier.java

registerForNotifications方法调用 registerForNewRingingConnection

4.1 调用 Phonebase中 不同的方法,注册为观察者。
翻译(不通顺 =_=):
我们应当直接跟phoneProxy做交互处理。然而phoneProxy直接与CallManager交互处理, 我们要么监听callmanager,要么就像这样参与到proxy中去。
两种都是不可取的,如果这个类和callmanager能够 register generically with the phone proxy instead ,这会比较好。
或者更好的是只是直接注册通知 with phone proxy, 而不用担心技术的改变,这需要改变opt/telephony中的代码。

         
/**
* Register for notifications from the base phone.
* TODO: We should only need to interact with the phoneproxy directly. However,
* since the phoneproxy only interacts directly with CallManager we either listen to callmanager
* or we have to poke into the proxy like this. Neither is desirable. It would be better if
* this class and callManager could register generically with the phone proxy instead and get
* radio techonology changes directly. Or better yet, just register for the notifications
* directly with phone proxy and never worry about the technology changes. This requires a
* change in opt/telephony code.
*/
private void registerForNotifications() {
Phone newPhone = mPhoneProxy.getActivePhone();
if (newPhone != mPhoneBase) {
unregisterForNotifications();
 
if (newPhone != null) {
Log.i(this, "Registering: %s", newPhone);
mPhoneBase = newPhone;
//调用 registerForNewRingingConnection方法
mPhoneBase.registerForNewRingingConnection(
mHandler, EVENT_NEW_RINGING_CONNECTION, null);
mPhoneBase.registerForCallWaiting(
mHandler, EVENT_CDMA_CALL_WAITING, null);
mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION,
null);
}
}
}
4.2 handle 处理EVENT_NEW_RINGING_CONNECTION消息

            
private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) {
          ...
                case EVENT_NEW_RINGING_CONNECTION :
                    handleNewRingingConnection((AsyncResult) msg.obj);
                    break;

4.2.1 handleNewRingingConnection方法,处理新的来电连接。
此处对应的log为:
D/Telephony( 1396): PstnIncomingCallNotifier:  handleNewRingingConnection

            
/**
* Verifies the incoming call and triggers sending the incoming-call intent to Telecom.
*
* @param asyncResult The result object from the new ringing event.
*/
private void handleNewRingingConnection(AsyncResult asyncResult) {
Log.d(this, "handleNewRingingConnection");
Connection connection = (Connection) asyncResult.result;
if (connection != null) {
Call call = connection.getCall();
 
// Final verification of the ringing state before sending the intent to Telecom.
//在发送intent到Telecom之前最后一次验证ringing 状态
if (call != null && call.getState().isRinging()) {
sendIncomingCallIntent(connection);
}
}
}
4.2.2 sendIncomingCallIntent方法
发送incoming call intent到telecom,发送的Connection 类型,里面包括isIncoming getState isRinging等

             
    /**
* Sends the incoming call intent to telecom.
*/
private void sendIncomingCallIntent(Connection connection) {
Bundle extras = null;
if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
!TextUtils.isEmpty(connection.getAddress())) {
extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
extras.putParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER, uri);
}
TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(
TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras);
}
addNewIncomingCall()定义在:  framworks/base/ telecomm /java/android/telecom/ TelecomManager.java   
TelecomManager的功能则主要是对TelecomService提供的远程接口的封装,然后提供给应用使用。
 addNewIncomingCall方法   @SystemApi
来电时触发此方法

              
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
* has an incoming call. The specified {@link PhoneAccountHandle?_?} must have been registered
* with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
* to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
* additional information about the call (See
* {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
*
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConnection}.
* @hide
*/
@SystemApi
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
getTelecomService().addNewIncomingCall(
phoneAccount, extras == null ? new Bundle() : extras);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException adding a new incoming call: " + phoneAccount, e);
}
}
addNewIncomingCall 的具体实现

Telecomm

5. packages/services/Telecomm/src/com/android/server/telecom/TelecomServiceImpl.java
继承自ITelecomService,TelecomService的接口由TeleComManager封装,并其供给应用使用,
5.1
@Override
addNewIncomingCall
新建intent 设定intent 的ACTION 、addFalgs等

        
  1. public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
  2. ...
  3. /**
  4. * @see android.telecom.TelecomManager#addNewIncomingCall
  5. */
  6. @Override
  7. public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
  8. if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {
  9. mAppOpsManager.checkPackage(
  10. Binder.getCallingUid(), phoneAccountHandle.getComponentName().getPackageName());

  11. Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
  12. intent.setPackage(mContext.getPackageName());
  13. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  14. intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
  15. if (extras != null) {
  16. intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
  17. }

  18. long token = Binder.clearCallingIdentity();
  19. //启动Activity
  20. mContext.startActivityAsUser(intent, UserHandle.CURRENT);
  21. Binder.restoreCallingIdentity(token);
  22. }
  23. }
//启动  CallActivity  对应的log:
I/ActivityManager(  852): START u0 {act=android.telecom.action.INCOMING_CALL flg=0x10000000 pkg=com.android.server.telecom cmp=com.android.server.telecom/.IncomingCallActivity (has extras)
} from uid 1001 on display 0


183<activity-alias android:name="IncomingCallActivity"184                android:targetActivity="CallActivity"185                android:exported="true">
186            <intent-filter>
187                <action android:name="android.telecom.action.INCOMING_CALL" />
188                <category android:name="android.intent.category.DEFAULT" />
189            </intent-filter>
190        </activity-alias>


6. packages/services/Telecomm/src/com/android/server/telecom/ CallActivity.java
6.1 执行完processIntent()后就finish(),对应的log
D/Telecom ( 1376): CallActivity:  onCreate: end

         
public class CallActivity extends Activity {
 
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
 
// TODO: Figure out if there is something to restore from bundle.
// See OutgoingCallBroadcaster in services/Telephony for more.
 
processIntent(getIntent());
 
// This activity does not have associated UI, so close.
finish();
Log.d(this, "onCreate: end");
}
6.1.1  processIntent 判断action

         
/**
* Processes intents sent to the activity.
*
* @param intent The intent.
*/
private void processIntent(Intent intent) {
// Ensure call intents are not processed on devices that are not capable of calling.
if (!isVoiceCapable()) {
return;
}
 
verifyCallAction(intent);
String action = intent.getAction();
 
if (Intent.ACTION_CALL.equals(action) ||
Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
Intent.ACTION_CALL_EMERGENCY.equals(action)) {
processOutgoingCallIntent(intent);
} else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) {
processIncomingCallIntent(intent);
}
}
6.1.2  processIncomingCallIntent

         
private void processIncomingCallIntent(Intent intent) {
if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
               CallReceiver . processIncomingCallIntent ( intent );
} else {
sendBroadcastToReceiver(intent, true /* isIncoming */);
}
}
7. packages/services/Telecomm/src/com/android/server/telecom/CallReceiver.java
7.1 对应log
D/Telecom ( 1376): com.android.server.telecom.CallReceiver: Processing incoming call from connection service [ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}]

          
static void processIncomingCallIntent(Intent intent) {
PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
 
if (phoneAccountHandle == null) {
Log.w(TAG, "Rejecting incoming call due to null phone account");
return;
}
if (phoneAccountHandle.getComponentName() == null) {
Log.w(TAG, "Rejecting incoming call due to null component name");
return;
}
 
Bundle clientExtras = null;
if (intent.hasExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS)) {
clientExtras = intent.getBundleExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS);
}
if (clientExtras == null) {
clientExtras = Bundle.EMPTY;
}
 
Log.d(TAG, "Processing incoming call from connection service [%s]",
phoneAccountHandle.getComponentName());
getCallsManager().processIncomingCallIntent(phoneAccountHandle, clientExtras);
}

8. packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java
8.1 processIncomingCallIntent
D/Telecom ( 1376): CallsManager: processIncomingCallIntent
new一个Call 对象 把前面的参数传进来,然后调用call中建立连接的方法 startCreateConnection
    /**
     * 开始把call attach到connection services
     *
     * @param phoneAccountHandle The phone account which contains the component name of the
     *        connection service to use for this call.
     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
     */


         
  1. /**
  2. * Starts the process to attach the call to a connection service.
  3. *
  4. * @param phoneAccountHandle The phone account which contains the component name of the
  5. * connection service to use for this call.
  6. * @param extras The optional extras Bundle passed with the intent used for the incoming call.
  7. */
  8. void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
  9. Log.d(this, "processIncomingCallIntent");
  10. Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
  11. Call call = new Call(
  12. mContext,
  13. mConnectionServiceRepository,
  14. handle,
  15. null /* gatewayInfo */,
  16. null /* connectionManagerPhoneAccount */,
  17. phoneAccountHandle,
  18. true /* isIncoming */,
  19. false /* isConference */);
  20. call.setExtras(extras);
  21. // TODO: Move this to be a part of addCall()
  22. call.addListener(this);
  23. call.startCreateConnection(mPhoneAccountRegistrar);
  24. }
④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager
    处理这个创建的连接>成功来电
⑤ 8>15>16>17     CallsManager>Phone
    成功来电>准备启动界面
⑥ 18>19>20>21>22 CallList>StatubarNotifier
    开始启动界面 显示来电
9. packages/services/Telecomm/src/com/android/server/telecom/Call.java
9.1 startCreateConnection()
开始建立连接队列,一旦完成创建,就应当有一个活动active的连接了存在service里。

         
  1. /**
  2. * Starts the create connection sequence. Upon completion, there should exist an active
  3. * connection through a connection service (or the call will have failed).
  4. *
  5. * @param phoneAccountRegistrar The phone account registrar.
  6. */
  7. void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
  8. Preconditions.checkState(mCreateConnectionProcessor == null);
  9. mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
  10. phoneAccountRegistrar, mContext);
  11. mCreateConnectionProcessor.process();
  12. }

③CreateConnectionProcesser>ConnectionServices 开始创建连接>创建连接完成。

10. packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

10.1 process

        
  1. void process() {
  2. Log.v(this, "process");
  3. mAttemptRecords = new ArrayList<>();
  4. if (mCall.getTargetPhoneAccount() != null) {
  5. mAttemptRecords.add(new CallAttemptRecord(
  6. mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
  7. }
  8. adjustAttemptsForConnectionManager();
  9. adjustAttemptsForEmergency();
  10. mAttemptRecordIterator = mAttemptRecords.iterator();
  11. attemptNextPhoneAccount();
  12. }
10.2 attemptNextPhoneAccount()
service试图建立连接

        
  1. private void attemptNextPhoneAccount() {
  2. ...
  3. if (mResponse != null && attempt != null) {
  4.             Log.i(this, "Trying attempt %s", attempt);
  5.             ConnectionServiceWrapper service =
  6.                     mRepository.getService(
  7.                             attempt.connectionManagerPhoneAccount.getComponentName());
  8.             if (service == null) {
  9.                 Log.i(this, "Found no connection service for attempt %s", attempt);
  10.                 attemptNextPhoneAccount();
  11.             } else {
  12.                 mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
  13.                 mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
  14.                 mCall.setConnectionService(service);
  15.                 Log.i(this, "Attempting to call from %s", service.getComponentName());
  16.                 service.createConnection(mCall, new Response(service));
  17.             }
  18.         } 



11.  packages/services/Telecomm/src/com/android/server/telecom/ ConnectionServiceWrapper.java  
11.1 createConnection() 
BindCallback 是interface
       /**
     * 为播出的电话建立连接,或者attach一个已经存在的来电。
     */

        
  1. /**
  2. * Creates a new connection for a new outgoing call or to attach to an existing incoming call.
  3. */
  4. void createConnection(final Call call, final CreateConnectionResponse response) {
  5. Log.d(this, "createConnection(%s) via %s.", call, getComponentName());
  6. BindCallback callback = new BindCallback() {
  7. @Override
  8. public void onSuccess() {
  9. String callId = mCallIdMapper.getCallId(call);
  10. mPendingResponses.put(callId, response);
  11. GatewayInfo gatewayInfo = call.getGatewayInfo();
  12. Bundle extras = call.getExtras();
  13. if (gatewayInfo != null && gatewayInfo.getGatewayProviderPackageName() != null &&
  14. gatewayInfo.getOriginalAddress() != null) {
  15. extras = (Bundle) extras.clone();
  16. extras.putString(
  17. TelecomManager.GATEWAY_PROVIDER_PACKAGE,
  18. gatewayInfo.getGatewayProviderPackageName());
  19. extras.putParcelable(
  20. TelecomManager.GATEWAY_ORIGINAL_ADDRESS,
  21. gatewayInfo.getOriginalAddress());
  22. }
  23. try {
  24. mServiceInterface.createConnection(
  25. call.getConnectionManagerPhoneAccount(),
  26. callId,
  27. new ConnectionRequest(
  28. call.getTargetPhoneAccount(),
  29. call.getHandle(),
  30. extras,
  31. call.getVideoState()),
  32. call.isIncoming(),
  33. call.isUnknown());
  34. } catch (RemoteException e) {
  35. Log.e(this, e, "Failure to createConnection -- %s", getComponentName());
  36. mPendingResponses.remove(callId).handleCreateConnectionFailure(
  37. new DisconnectCause(DisconnectCause.ERROR, e.toString()));
  38. }
  39. }
  40. @Override
  41. public void onFailure() {
  42. Log.e(this, new Exception(), "Failure to call %s", getComponentName());
  43. response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));
  44. }
  45. };
  46. mBinder.bind(callback);
  47. }
12. packages/services/Telecomm/src/com/android/server/telecom/Servicesbinder.java
12.1  bind 绑定连接
          /**
         * 执行绑定到服务的操作(如果还没有绑定)然后执行指定的回调方法
         *
         * @param callback The 回调方法通知绑定是成功或失败
         */

        
  1. /**
  2. * Helper class to perform on-demand binding.
  3. */
  4. final class Binder {
  5. /**
  6. * Performs an bind to the service (only if not already bound) and executes the
  7. * specified callback.
  8. *
  9. * @param callback The callback to notify of the binding's success or failure.
  10. */
  11. void bind(BindCallback callback) {
  12. ThreadUtil.checkOnMainThread();
  13. Log.d(ServiceBinder.this, "bind()");

  14. // Reset any abort request if we're asked to bind again.
  15. clearAbort();

  16. if (!mCallbacks.isEmpty()) {
  17. // Binding already in progress, append to the list of callbacks and bail out.
  18. mCallbacks.add(callback);
  19. return;
  20. }

  21. mCallbacks.add(callback);
  22. if (mServiceConnection == null) {
  23. Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
  24. ServiceConnection connection = new ServiceBinderConnection();

  25. Log.d(ServiceBinder.this, "Binding to service with intent: %s", serviceIntent);
  26. if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
  27. handleFailedConnection();
  28. return;
  29. }
  30. } else {
  31. Log.d(ServiceBinder.this, "Service is already bound.");
  32. Preconditions.checkNotNull(mBinder);
  33. handleSuccessfulConnection();
  34. }
  35. }
  36. }
  37. //
上面的执行完之后,顺序执行到 onServiceConnected
12.2 onServiceConnected

         
  1. private final class ServiceBinderConnection implements ServiceConnection {
  2. @Override
  3. public void onServiceConnected(ComponentName componentName, IBinder binder) {
  4. ThreadUtil.checkOnMainThread();
  5. Log.i(this, "Service bound %s", componentName);//这句log被打印出来了
  6. // Unbind request was queued so unbind immediately.
  7. if (mIsBindingAborted) {
  8. clearAbort();
  9. logServiceDisconnected("onServiceConnected");
  10. mContext.unbindService(this);
  11. handleFailedConnection();
  12. return;
  13. }
  14. mServiceConnection = this;
  15. setBinder(binder);
  16. handleSuccessfulConnection();
  17. }
handleSuccessfulConnection

         
  1. private void handleSuccessfulConnection() {
  2. for (BindCallback callback : mCallbacks) {
  3. callback.onSuccess();
  4. }
  5. mCallbacks.clear();
  6. }

回调上面的onSuccess() 执行 mServiceInterface . createConnection  createConnection的具体实现在
13. framworks/base/telecomm/java/android/telecom/ConnectionServices.java


         
  1. @Override
  2. public void createConnection(
  3. PhoneAccountHandle connectionManagerPhoneAccount,
  4. String id,
  5. ConnectionRequest request,
  6. boolean isIncoming,
  7. boolean isUnknown) {
  8. //chengzhi
  9. SomeArgs args = SomeArgs.obtain();
  10. args.arg1 = connectionManagerPhoneAccount;
  11. args.arg2 = id;
  12. args.arg3 = request;
  13. args.argi1 = isIncoming ? 1 : 0;
  14. args.argi2 = isUnknown ? 1 : 0;
  15. mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
  16. }
13.1 handleMessage() MSG_CREATE_CONNECTION

         
  1. case MSG_CREATE_CONNECTION: {
  2. SomeArgs args = (SomeArgs) msg.obj;
  3. try {
  4. //chengzhi
  5. final PhoneAccountHandle connectionManagerPhoneAccount =
  6. (PhoneAccountHandle) args.arg1;
  7. final String id = (String) args.arg2;
  8. final ConnectionRequest request = (ConnectionRequest) args.arg3;
  9. final boolean isIncoming = args.argi1 == 1;
  10. final boolean isUnknown = args.argi2 == 1;
  11. if (!mAreAccountsInitialized) {
  12. Log.d(this, "Enqueueing pre-init request %s", id);
  13. mPreInitializationConnectionRequests.add(new Runnable() {
  14. @Override
  15. public void run() {
  16. createConnection(
  17. connectionManagerPhoneAccount,
  18. id,
  19. request,
  20. isIncoming,
  21. isUnknown);
  22. }
  23. });
  24. } else {
  25. //chengzhi debug
  26. createConnection(
  27. connectionManagerPhoneAccount,
  28. id,
  29. request,
  30. isIncoming,
  31. isUnknown);
  32. }
  33. } finally {
  34. args.recycle();
  35. }
  36. break;
  37. }
13.1.1 createConnection方法
创建 Connection connection
    /**
     * 这个方法可以被telecom用来创建呼出电话或者一个已存在的来电。任何一种情况,telecom都会循环经过一系列的服务和 调用 createConnection util a connection service取消或者成功完成创建。
     */

         
  1. /**
  2. * This can be used by telecom to either create a new outgoing call or attach to an existing
  3. * incoming call. In either case, telecom will cycle through a set of services and call
  4. * createConnection util a connection service cancels the process or completes it successfully.
  5. */
  6. private void createConnection(
  7. final PhoneAccountHandle callManagerAccount,
  8. final String callId,
  9. final ConnectionRequest request,
  10. boolean isIncoming,
  11. boolean isUnknown) {
  12. Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
  13. "isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request, isIncoming,
  14. isUnknown);
  15. //chengzhi 01
  16. Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
  17. : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
  18. : onCreateOutgoingConnection(callManagerAccount, request);
  19. .....
  20.         mAdapter.handleCreateConnectionComplete
前面建立连接成功了,后面处理成功的连接
后面执行 mAdapter.handleCreateConnectionComplete

④ 14>11>9>>8 ConnectionServicesAdapter>CallsManager  处理这个创建的连接>成功来电

14.framework/base/telecomm/java/android/telecom/ConnectionServicesAdapter.java

         
  1. void handleCreateConnectionComplete(
  2. String id,
  3. ConnectionRequest request,
  4. ParcelableConnection connection) {
  5. for (IConnectionServiceAdapter adapter : mAdapters) {
  6. try {
  7. //chengzhi 03
  8. adapter.handleCreateConnectionComplete(id, request, connection);
  9. } catch (RemoteException e) {
  10. }
  11. }
  12. }

⑤  CallsManager>Phone    成功来电>准备启动界面

15. //packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java   handleCreateConnectionComplete

           
  1. private final class Adapter extends IConnectionServiceAdapter.Stub {
  2. @Override
  3. public void handleCreateConnectionComplete(
  4. String callId,
  5. ConnectionRequest request,
  6. ParcelableConnection connection) {
  7. logIncoming("handleCreateConnectionComplete %s", request);
  8. if (mCallIdMapper.isValidCallId(callId)) {
  9. SomeArgs args = SomeArgs.obtain();
  10. args.arg1 = callId;
  11. args.arg2 = request;
  12. args.arg3 = connection;
  13. mHandler.obtainMessage(MSG_HANDLE_CREATE_CONNECTION_COMPLETE, args)
  14. .sendToTarget();
  15. }
  16. }
15.2 handleMessage处理消息  MSG_HANDLE_CREATE_CONNECTION_COMPLETE

            
  1. private final Handler mHandler = new Handler() {
  2. @Override
  3. public void handleMessage(Message msg) {
  4. Call call;
  5. switch (msg.what) {
  6. case MSG_HANDLE_CREATE_CONNECTION_COMPLETE: {
  7. SomeArgs args = (SomeArgs) msg.obj;
  8. try {
  9. String callId = (String) args.arg1;
  10. ConnectionRequest request = (ConnectionRequest) args.arg2;
  11. ParcelableConnection connection = (ParcelableConnection) args.arg3;
  12. handleCreateConnectionComplete(callId, request, connection);
  13. } finally {
  14. args.recycle();
  15. }
  16. break;
  17. }
15.2.1 handleCreateConnectionComplete ()
如果成功连接

            
  1. private void handleCreateConnectionComplete(
  2. String callId,
  3. ConnectionRequest request,
  4. ParcelableConnection connection) {
  5. // TODO: Note we are not using parameter "request", which is a side effect of our tacit
  6. // assumption that we have at most one outgoing connection attempt per ConnectionService.
  7. // This may not continue to be the case.
  8. if (connection.getState() == Connection.STATE_DISCONNECTED) {
  9. // A connection that begins in the DISCONNECTED state is an indication of
  10. // failure to connect; we handle all failures uniformly
  11. removeCall(callId, connection.getDisconnectCause());
  12. } else {
  13. // Successful connection
  14. if (mPendingResponses.containsKey(callId)) {
  15. mPendingResponses.remove(callId)
  16. .handleCreateConnectionSuccess(mCallIdMapper, connection);
  17. }
  18. }
  19. }
重写  handleCreateConnectionSuccess方法
16. //packages/services/Telecomm/src/com/android/server/telecom/ Call.java 
16.1 handleCreateConnetionSucess()

             
@Override
public void handleCreateConnectionSuccess(
CallIdMapper idMapper,
ParcelableConnection connection) {
Log.v(this, "handleCreateConnectionSuccessful %s", connection);
mCreateConnectionProcessor = null;
setTargetPhoneAccount(connection.getPhoneAccount());
setHandle(connection.getHandle(), connection.getHandlePresentation());
setCallerDisplayName(
connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
setCallCapabilities(connection.getCapabilities());
setVideoProvider(connection.getVideoProvider());
setVideoState(connection.getVideoState());
setRingbackRequested(connection.isRingbackRequested());
setIsVoipAudioMode(connection.getIsVoipAudioMode());
setStatusHints(connection.getStatusHints());
 
mConferenceableCalls.clear();
for (String id : connection.getConferenceableConnectionIds()) {
mConferenceableCalls.add(idMapper.getCall(id));
}
 
if (mIsUnknown) {
for (Listener l : mListeners) {
l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
}
} else if (mIsIncoming) {
// We do not handle incoming calls immediately when they are verified by the connection
// service. We allow the caller-info-query code to execute first so that we can read the
// direct-to-voicemail property before deciding if we want to show the incoming call to
// the user or if we want to reject the call.
mDirectToVoicemailQueryPending = true;
 
// Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
// showing the user the incoming call screen.
mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
mContext.getContentResolver()));
} else {
for (Listener l : mListeners) {
l.onSuccessfulOutgoingCall(this,
getStateFromConnectionState(connection.getState()));
}
}
}

16.2 Runnable mDirectToVoicemailRunnable

            
  1. private final Runnable mDirectToVoicemailRunnable = new Runnable() {
  2. @Override
  3. public void run() {
  4. processDirectToVoicemail();
  5. }
16.2.1 processDirectToVoicemail

             
  1. final class Call implements CreateConnectionResponse {
  2. /**
  3. * Listener for events on the call.
  4. */
  5. interface Listener {
  6. void onSuccessfulIncomingCall(Call call);
  7. ...
  8. private void processDirectToVoicemail() {
  9. if (mDirectToVoicemailQueryPending) {
  10. if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) {
  11. Log.i(this, "Directing call to voicemail: %s.", this);
  12. // TODO: Once we move State handling from CallsManager to Call, we
  13. // will not need to set STATE_RINGING state prior to calling reject.
  14. setState(CallState.RINGING);
  15. reject(false, null);
  16. } else {
  17. // TODO: Make this class (not CallsManager) responsible for changing
  18. // the call state to STATE_RINGING.
  19. // TODO: Replace this with state transition to STATE_RINGING.
  20. for (Listener l : mListeners) {
  21. l.onSuccessfulIncomingCall(this);
  22. }
  23. }
  24. mDirectToVoicemailQueryPending = false;
  25. }
  26. }

17. //package/services/Telecomm/src/com/android/server/telecom/CallsManager.java  
17.1
@Override onSuccessfulIncomingCall
if 判断后 addCall()

            
  1. public final class CallsManager extends Call.ListenerBase {
  2. ...
  3. @Override
  4. public void onSuccessfulIncomingCall(Call incomingCall) {
  5. Log.d(this, "onSuccessfulIncomingCall");
  6. setCallState(incomingCall, CallState.RINGING);
  7. if (hasMaximumRingingCalls(incomingCall.getTargetPhoneAccount().getId())) {
  8. incomingCall.reject(false, null);
  9. // since the call was not added to the list of calls, we have to call the missed
  10. // call notifier and the call logger manually.
  11. mMissedCallNotifier.showMissedCallNotification(incomingCall);
  12. mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
  13. } else {
  14. incomingCall.mIsActiveSub = true;
  15. addCall(incomingCall);
  16. setActiveSubscription(incomingCall.getTargetPhoneAccount().getId());
  17. }
  18. }
17.1.1 addCall()

            
  1. /**
  2. * Adds the specified call to the main list of live calls.
  3. *
  4. * @param call The call to add.
  5. */
  6. private void addCall(Call call) {
  7. Log.v(this, "addCall(%s)", call);

  8. call.addListener(this);
  9. mCalls.add(call);

  10. // TODO: Update mForegroundCall prior to invoking
  11. // onCallAdded for calls which immediately take the foreground (like the first call).
  12. for (CallsManagerListener listener : mListeners) {
  13. listener.onCallAdded(call);
  14. }
  15. updateForegroundCall();
  16. }


InCallUI

⑥ CallList>StatubarNotifier    开始启动界面 显示来电

18. //package/services/Telecomm/src/com/android/server/telecom/ InCallController.java  
18.1 重写onCallAdded
@Override onCallAdded()

             
  1. @Override
  2. public void onCallAdded(Call call) {
  3. if (mInCallServices.isEmpty()) {
  4. bind();//执行这里
  5. } else {
  6. Log.i(this, "onCallAdded: %s", call);//输出log
  7. // Track the call if we don't already know about it.
  8. addCall(call);
  9. for (Map.Entry<ComponentName, IInCallService> entry : mInCallServices.entrySet()) {
  10. ComponentName componentName = entry.getKey();
  11. IInCallService inCallService = entry.getValue();
  12. ParcelableCall parcelableCall = toParcelableCall(call,
  13. componentName.equals(mInCallComponentName) /* includeVideoProvider */);
  14. try {
  15. inCallService.addCall(parcelableCall);
  16. } catch (RemoteException ignored) {
  17. }
  18. }
  19. }
  20. }
18.1.1 bind()
InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();

           
  1. /**
  2. * Binds to the in-call app if not already connected by binding directly to the saved
  3. * component name of the {@link IInCallService} implementation.
  4. */
  5. private void bind() {
  6. ThreadUtil.checkOnMainThread();
  7. if (mInCallServices.isEmpty()) {
  8. PackageManager packageManager = mContext.getPackageManager();
  9. Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);

  10. for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {
  11. ServiceInfo serviceInfo = entry.serviceInfo;
  12. if (serviceInfo != null) {
  13. boolean hasServiceBindPermission = serviceInfo.permission != null &&
  14. serviceInfo.permission.equals(
  15. Manifest.permission.BIND_INCALL_SERVICE);
  16. boolean hasControlInCallPermission = packageManager.checkPermission(
  17. Manifest.permission.CONTROL_INCALL_EXPERIENCE,
  18. serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED;

  19. if (!hasServiceBindPermission) {
  20. Log.w(this, "InCallService does not have BIND_INCALL_SERVICE permission: " +
  21. serviceInfo.packageName);
  22. continue;
  23. }

  24. if (!hasControlInCallPermission) {
  25. Log.w(this,
  26. "InCall UI does not have CONTROL_INCALL_EXPERIENCE permission: " +
  27. serviceInfo.packageName);
  28. continue;
  29. }

  30. InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();
  31. ComponentName componentName = new ComponentName(serviceInfo.packageName,
  32. serviceInfo.name);

  33. Log.i(this, "Attempting to bind to InCall %s, is dupe? %b ", //log输出
  34. serviceInfo.packageName,
  35. mServiceConnections.containsKey(componentName));

  36. if (!mServiceConnections.containsKey(componentName)) {
  37. Intent intent = new Intent(InCallService.SERVICE_INTERFACE);
  38. intent.setComponent(componentName);

  39. if (mContext.bindServiceAsUser(intent, inCallServiceConnection,
  40. Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
  41. mServiceConnections.put(componentName, inCallServiceConnection);
  42. }
  43. }
  44. }
  45. }
  46. }
  47. }
18.1.2 InCallServiceConnection inCallServiceConnection = new InCallServiceConnection ();
InCallServiceConnection

           
  1. /**
  2. * Used to bind to the in-call app and triggers the start of communication between
  3. * this class and in-call app.
  4. */
  5. private class InCallServiceConnection implements ServiceConnection {
  6. /** {@inheritDoc} */
  7. @Override public void onServiceConnected(ComponentName name, IBinder service) {
  8. Log.d(this, "onServiceConnected: %s", name);
  9. onConnected(name, service);
  10. }
  11. /** {@inheritDoc} */
  12. @Override public void onServiceDisconnected(ComponentName name) {
  13. Log.d(this, "onDisconnected: %s", name);
  14. onDisconnected(name);
  15. }
  16. }
18.1.3 onConnected

            
  1. /**
  2. * Persists the {@link IInCallService} instance and starts the communication between
  3. * this class and in-call app by sending the first update to in-call app. This method is
  4. * called after a successful binding connection is established.
  5. *
  6. * @param componentName The service {@link ComponentName}.
  7. * @param service The {@link IInCallService} implementation.
  8. */
  9. private void onConnected(ComponentName componentName, IBinder service) {
  10. ThreadUtil.checkOnMainThread();

  11. Log.i(this, "onConnected to %s", componentName);

  12. IInCallService inCallService = IInCallService.Stub.asInterface(service);

  13. try {
  14. inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),
  15. mCallIdMapper));
  16. mInCallServices.put(componentName, inCallService);
  17. } catch (RemoteException e) {
  18. Log.e(this, e, "Failed to set the in-call adapter.");
  19. return;
  20. }

  21. // Upon successful connection, send the state of the world to the service.
  22. ImmutableCollection<Call> calls = CallsManager.getInstance().getCalls();
  23. if (!calls.isEmpty()) {
  24. Log.i(this, "Adding %s calls to InCallService after onConnected: %s", calls.size(),
  25. componentName);
  26. for (Call call : calls) {
  27. try {
  28. // Track the call if we don't already know about it.
  29. Log.i(this, "addCall after binding: %s", call);
  30. addCall(call);

  31. inCallService.addCall(toParcelableCall(call,
  32. componentName.equals(mInCallComponentName) /* includeVideoProvider */));
  33. } catch (RemoteException ignored) {
  34. }
  35. }
  36. onAudioStateChanged(null, CallsManager.getInstance().getAudioState());
  37. } else {
  38. unbind();
  39. }
  40. }
addCall(call)

19. //framworks/base/telecomm/java/android/telecom/InCallService.java 
@Override 
addCall()

            
  1. /** Manages the binder calls so that the implementor does not need to deal with it. */
  2. private final class InCallServiceBinder extends IInCallService.Stub {
  3. @Override
  4. public void setInCallAdapter(IInCallAdapter inCallAdapter) {
  5. mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();
  6. }
  7. @Override
  8. public void addCall(ParcelableCall call) {
  9. mHandler.obtainMessage(MSG_ADD_CALL, call).sendToTarget();
  10. }
19.1 handleMessage 处理消息 MSG_ADD_CALL

            
  1. /** Default Handler used to consolidate binder method calls onto a single thread. */
  2. private final Handler mHandler = new Handler(Looper.getMainLooper()) {
  3. @Override
  4. public void handleMessage(Message msg) {
  5. if (mPhone == null && msg.what != MSG_SET_IN_CALL_ADAPTER) {
  6. return;
  7. }

  8. switch (msg.what) {
  9. case MSG_SET_IN_CALL_ADAPTER:
  10. mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
  11. onPhoneCreated(mPhone);
  12. break;
  13. case MSG_ADD_CALL:
  14. mPhone.internalAddCall((ParcelableCall) msg.obj);
  15. break;

21. //framworks/base/telecomm/java/android/telecom/Phone.java
internalAddCall()

            
  1. final void internalAddCall(ParcelableCall parcelableCall) {
  2. Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
  3. parcelableCall.mIsActiveSub);
  4. mCallByTelecomCallId.put(parcelableCall.getId(), call);
  5. mCalls.add(call);
  6. checkCallTree(parcelableCall);
  7. call.internalUpdate(parcelableCall, mCallByTelecomCallId);
  8. fireCallAdded(call);
  9. }
20.1.1 fireCallAdded()

            
  1. private void fireCallAdded(Call call) {
  2. for (Listener listener : mListeners) {
  3. listener.onCallAdded(this, call);
  4. }
  5. }

onCallAdded()  @SystemApi系统Api  其他使用的地方会 @Override

             
  1. @SystemApipublic final class Phone { public abstract static class Listener {
  2.     ...
  3.     public void onCallAdded(Phone phone, Call call) { }
接22_2.
22_1. //pacakge/apps/InCallUI/src/com/android/incallui/ CallList.java  
@Override 
onCallAdded

             
  1. /**
  2. * Static singleton accessor method.
  3. */
  4. public static CallList getInstance() {
  5. return sInstance;
  6. }

  7. private Phone.Listener mPhoneListener = new Phone.Listener() {
  8. @Override
  9. public void onCallAdded(Phone phone, android.telecom.Call telecommCall) {
  10. Call call = new Call(telecommCall);
  11. if (call.getState() == Call.State.INCOMING) {
  12. onIncoming(call, call.getCannedSmsResponses());
  13. } else {
  14. onUpdate(call);
  15. }
  16. }
执行了下面的方法,但再往后的步骤不是从这里走的。
onIncoming()

              
  1. /**
  2. * Called when a single call has changed.
  3. */
  4. public void onIncoming(Call call, List<String> textMessages) {
  5. Log.d(this, "onIncoming - " + call);
  6. // Update active subscription from call object. it will be set by
  7. // Telecomm service for incoming call and whenever active sub changes.
  8. if (call.mIsActiveSub) {
  9. long sub = call.getSubId();
  10. Log.d(this, "onIncoming - sub:" + sub + " mSubId:" + mSubId);
  11. if (sub != mSubId) {
  12. setActiveSubscription(sub);
  13. }
  14. }
  15. if (updateCallInMap(call)) {
  16. Log.i(this, "onIncoming - " + call);
  17. }
  18. updateCallTextMap(call, textMessages);
  19. for (Listener listener : mListeners) {
  20. listener.onIncomingCall(call);
  21. }
  22. }
22_2. //pacakge/apps/InCallUI/src/com/android/incallui/InCallPresenter.java  
@Override 
onCallAdded

              
  1. public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
  2. ...
  3. private final Phone.Listener mPhoneListener = new Phone.Listener() {
  4. ...
  5.  
  6.         @Override
  7. public void onCallAdded(Phone phone, android.telecom.Call call) {
  8. call.addListener(mCallListener);
  9. }

23. //pacakge/apps/InCallUI/src/com/android/incallui/InCallPresenter.java  @override onCallAdded

onIncomingCall

              
  1. /**
  2. * Called when there is a new incoming call.
  3. *
  4. * @param call
  5. */
  6. @Override
  7. public void onIncomingCall(Call call) {
  8. InCallState newState = startOrFinishUi(InCallState.INCOMING);
  9. InCallState oldState = mInCallState;
  10. Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
  11. mInCallState = newState;
  12. for (IncomingCallListener listener : mIncomingCallListeners) {
  13. listener.onIncomingCall(oldState, mInCallState, call);
  14. }
  15. if (CallList.getInstance().isDsdaEnabled() && (mInCallActivity != null)) {
  16. mInCallActivity.updateDsdaTab();
  17. }
  18. }
startOrFinishUi() 开始或结束UI

              
  1. /**
  2. * When the state of in-call changes, this is the first method to get called. It determines if
  3. * the UI needs to be started or finished depending on the new state and does it.
  4. */
  5. private InCallState startOrFinishUi(InCallState newState) {
  6. Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState);

  7. // TODO: Consider a proper state machine implementation

  8. // If the state isn't changing or if we're transitioning from pending outgoing to actual
  9. // outgoing, we have already done any starting/stopping of activities in a previous pass
  10. // ...so lets cut out early
  11. boolean alreadyOutgoing = mInCallState == InCallState.PENDING_OUTGOING &&
  12. newState == InCallState.OUTGOING;
  13. boolean isAnyOtherSubActive = InCallState.INCOMING == newState &&
  14. mCallList.isAnyOtherSubActive(mCallList.getActiveSubscription());
  15. if ((newState == mInCallState && !(mInCallActivity == null && isAnyOtherSubActive))
  16. || alreadyOutgoing) {
  17. return newState;
  18. }

  19. // A new Incoming call means that the user needs to be notified of the the call (since
  20. // it wasn't them who initiated it). We do this through full screen notifications and
  21. // happens indirectly through {@link StatusBarNotifier}.
  22. //
  23. // The process for incoming calls is as follows:
  24. //
  25. // 1) CallList - Announces existence of new INCOMING call
  26. // 2) InCallPresenter - Gets announcement and calculates that the new InCallState
  27. // - should be set to INCOMING.
  28. // 3) InCallPresenter - This method is called to see if we need to start or finish
  29. // the app given the new state.
  30. // 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls
  31. // StatusBarNotifier explicitly to issue a FullScreen Notification
  32. // that will either start the InCallActivity or show the user a
  33. // top-level notification dialog if the user is in an immersive app.
  34. // That notification can also start the InCallActivity.
  35. // 5) InCallActivity - Main activity starts up and at the end of its onCreate will
  36. // call InCallPresenter::setActivity() to let the presenter
  37. // know that start-up is complete.
  38. //
  39. // [ AND NOW YOU'RE IN THE CALL. voila! ]
  40. //
  41. // Our app is started using a fullScreen notification. We need to do this whenever
  42. // we get an incoming call.
  43. final boolean startStartupSequence = (InCallState.INCOMING == newState);

  44. // A dialog to show on top of the InCallUI to select a PhoneAccount
  45. final boolean showAccountPicker = (InCallState.WAITING_FOR_ACCOUNT == newState);

  46. // A new outgoing call indicates that the user just now dialed a number and when that
  47. // happens we need to display the screen immediately or show an account picker dialog if
  48. // no default is set. However, if the main InCallUI is already visible, we do not want to
  49. // re-initiate the start-up animation, so we do not need to do anything here.
  50. //
  51. // It is also possible to go into an intermediate state where the call has been initiated
  52. // but Telecomm has not yet returned with the details of the call (handle, gateway, etc.).
  53. // This pending outgoing state can also launch the call screen.
  54. //
  55. // This is different from the incoming call sequence because we do not need to shock the
  56. // user with a top-level notification. Just show the call UI normally.
  57. final boolean mainUiNotVisible = !isShowingInCallUi() || !getCallCardFragmentVisible();
  58. final boolean showCallUi = ((InCallState.PENDING_OUTGOING == newState ||
  59. InCallState.OUTGOING == newState) && mainUiNotVisible);

  60. // TODO: Can we be suddenly in a call without it having been in the outgoing or incoming
  61. // state? I havent seen that but if it can happen, the code below should be enabled.
  62. // showCallUi |= (InCallState.INCALL && !isActivityStarted());

  63. // The only time that we have an instance of mInCallActivity and it isn't started is
  64. // when it is being destroyed. In that case, lets avoid bringing up another instance of
  65. // the activity. When it is finally destroyed, we double check if we should bring it back
  66. // up so we aren't going to lose anything by avoiding a second startup here.
  67. boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted();
  68. if (activityIsFinishing) {
  69. Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState);
  70. return mInCallState;
  71. }

  72. if (showCallUi || showAccountPicker) {
  73. Log.i(this, "Start in call UI");
  74. showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */);
  75. } 
  76.         
  77.         //如果是来电的话
  78.         else if (startStartupSequence) {
  79. Log.i(this, "Start Full Screen in call UI");

  80. // We're about the bring up the in-call UI for an incoming call. If we still have
  81. // dialogs up, we need to clear them out before showing incoming screen.
  82. if (isActivityStarted()) {
  83. mInCallActivity.dismissPendingDialogs();
  84. }
  85. if (!startUi(newState)) {
  86. // startUI refused to start the UI. This indicates that it needed to restart the
  87. // activity. When it finally restarts, it will call us back, so we do not actually
  88. // change the state yet (we return mInCallState instead of newState).
  89. return mInCallState;
  90. }
  91. } else if (newState == InCallState.NO_CALLS) {
  92. // The new state is the no calls state. Tear everything down.
  93. attemptFinishActivity();
  94. attemptCleanup();
  95. }

  96. return newState;
  97. }
startUi 创建UI

              
  1. private boolean startUi(InCallState inCallState) {
  2. final Call incomingCall = mCallList.getIncomingCall();
  3. boolean isCallWaiting = mCallList.getActiveCall() != null &&
  4. mCallList.getIncomingCall() != null;
  5. // If the screen is off, we need to make sure it gets turned on for incoming calls.
  6. // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
  7. // when the activity is first created. Therefore, to ensure the screen is turned on
  8. // for the call waiting case, we finish() the current activity and start a new one.
  9. // There should be no jank from this since the screen is already off and will remain so
  10. // until our new activity is up.
  11. // In addition to call waiting scenario, we need to force finish() in case of DSDA when
  12. // we get an incoming call on one sub and there is a live call in other sub and screen
  13. // is off.
  14. boolean anyOtherSubActive = (incomingCall != null &&
  15. mCallList.isAnyOtherSubActive(mCallList.getActiveSubscription()));
  16. Log.i(this, "Start UI " + " anyOtherSubActive:" + anyOtherSubActive);
  17. if (isCallWaiting || anyOtherSubActive) {
  18. if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) {
  19. mInCallActivity.finish();
  20. // When the activity actually finishes, we will start it again if there are
  21. // any active calls, so we do not need to start it explicitly here. Note, we
  22. // actually get called back on this function to restart it.
  23. // We return false to indicate that we did not actually start the UI.
  24. return false;
  25. } else {
  26. showInCall(false, false);
  27. }
  28. } else {
  29. mStatusBarNotifier.updateNotification(inCallState, mCallList);
  30. }
  31. return true;
  32. }
23. //pakages/apps/InCallUI/src/com/android/incallui/StatuBarNotifier.java       updateNotification()

              
  1. /**
  2. * Updates the phone app's status bar notification *and* launches the
  3. * incoming call UI in response to a new incoming call.
  4. *
  5. * If an incoming call is ringing (or call-waiting), the notification
  6. * will also include a "fullScreenIntent" that will cause the
  7. * InCallScreen to be launched, unless the current foreground activity
  8. * is marked as "immersive".
  9. *
  10. * (This is the mechanism that actually brings up the incoming call UI
  11. * when we receive a "new ringing connection" event from the telephony
  12. * layer.)
  13. *
  14. * Also note that this method is safe to call even if the phone isn't
  15. * actually ringing (or, more likely, if an incoming call *was*
  16. * ringing briefly but then disconnected). In that case, we'll simply
  17. * update or cancel the in-call notification based on the current
  18. * phone state.
  19. *
  20. * @see #updateInCallNotification(InCallState,CallList)
  21. */
  22. public void updateNotification(InCallState state, CallList callList) {
  23. updateInCallNotification(state, callList);
  24. }



updateInCallNotification()

              
  1. /**
  2. * Helper method for updateInCallNotification() and
  3. * updateNotification(): Update the phone app's
  4. * status bar notification based on the current telephony state, or
  5. * cancels the notification if the phone is totally idle.
  6. */
  7. private void updateInCallNotification(final InCallState state, CallList callList) {
  8. Log.d(this, "updateInCallNotification...");
  9. Call call = getCallToShow(callList);
  10. // Whether we have an outgoing call but the incall UI has yet to show up.
  11. // Since we don't normally show a notification while the incall screen is
  12. // in the foreground, if we show the outgoing notification before the activity
  13. // comes up the user will see it flash on and off on an outgoing call. We therefore
  14. // do not show the notification for outgoing calls before the activity has started.
  15. boolean isOutgoingWithoutIncallUi =
  16. state == InCallState.OUTGOING &&
  17. !InCallPresenter.getInstance().isActivityPreviouslyStarted();
  18. // Whether to show a notification immediately.
  19. boolean showNotificationNow =
  20. // We can still be in the INCALL state when a call is disconnected (in order to show
  21. // the "Call ended" screen. So check that we have an active connection too.
  22. (call != null) &&
  23. // We show a notification iff there is an active call.
  24. state.isConnectingOrConnected() &&
  25. // If the UI is already showing, then for most cases we do not want to show
  26. // a notification since that would be redundant, unless it is an incoming call,
  27. // in which case the notification is actually an important alert.
  28. (!InCallPresenter.getInstance().isShowingInCallUi() || state.isIncoming()) &&
  29. // If we have an outgoing call with no UI but the timer has fired, we show
  30. // a notification anyway.
  31. (!isOutgoingWithoutIncallUi ||
  32. mNotificationTimer.getState() == NotificationTimer.State.FIRED);
  33. if (showNotificationNow) {
  34. showNotification(call);
  35. } else {
  36. cancelInCall();
  37. if (isOutgoingWithoutIncallUi &&
  38. mNotificationTimer.getState() == NotificationTimer.State.CLEAR) {
  39. mNotificationTimer.schedule();
  40. }
  41. }
  42. // If we see a UI, or we are done with calls for now, reset to ground state.
  43. if (InCallPresenter.getInstance().isShowingInCallUi() || call == null) {
  44. mNotificationTimer.clear();
  45. }
  46. }

               
  1. private void showNotification(final Call call) {
  2. final boolean isIncoming = (call.getState() == Call.State.INCOMING ||
  3. call.getState() == Call.State.CALL_WAITING);

  4. // we make a call to the contact info cache to query for supplemental data to what the
  5. // call provides. This includes the contact name and photo.
  6. // This callback will always get called immediately and synchronously with whatever data
  7. // it has available, and may make a subsequent call later (same thread) if it had to
  8. // call into the contacts provider for more data.
  9. mContactInfoCache.findInfo(call, isIncoming, new ContactInfoCacheCallback() {
  10. @Override
  11. public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
  12. Call call = CallList.getInstance().getCallById(callId);
  13. if (call != null) {
  14. buildAndSendNotification(call, entry);
  15. }
  16. }

  17. @Override
  18. public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
  19. Call call = CallList.getInstance().getCallById(callId);
  20. if (call != null) {
  21. buildAndSendNotification(call, entry);
  22. }
  23. }
  24. });
  25. }
构建heads-ip notification通知

               
  1. /**
  2. * Sets up the main Ui for the notification
  3. */
  4. private void buildAndSendNotification(Call originalCall, ContactCacheEntry contactInfo) {
  5. // This can get called to update an existing notification after contact information has come
  6. // back. However, it can happen much later. Before we continue, we need to make sure that
  7. // the call being passed in is still the one we want to show in the notification.
  8. final Call call = getCallToShow(CallList.getInstance());
  9. if (call == null || !call.getId().equals(originalCall.getId())) {
  10. return;
  11. }
  12. final int state = call.getState();
  13. final boolean isConference = call.isConferenceCall();
  14. final boolean isVideoUpgradeRequest = call.getSessionModificationState()
  15. == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
  16. // Check if data has changed; if nothing is different, don't issue another notification.
  17. final int iconResId = getIconToDisplay(call);
  18. final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, isConference);
  19. final int contentResId = getContentString(call);
  20. final String contentTitle = getContentTitle(contactInfo, isConference);
  21. if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state)) {
  22. return;
  23. }
  24. /*
  25. * Nothing more to check...build and send it.
  26. */
  27. final Notification.Builder builder = getNotificationBuilder();
  28. // Set up the main intent to send the user to the in-call screen
  29. final PendingIntent inCallPendingIntent = createLaunchPendingIntent();
  30. builder.setContentIntent(inCallPendingIntent);
  31. // Set the intent as a full screen intent as well if a call is incoming
  32. if ((state == Call.State.INCOMING || state == Call.State.CALL_WAITING) &&
  33. !InCallPresenter.getInstance().isShowingInCallUi()) {
  34. configureFullScreenIntent(builder, inCallPendingIntent, call);
  35. }
  36. // Set the content
  37. builder.setContentText(mContext.getString(contentResId));
  38. builder.setSmallIcon(iconResId);
  39. builder.setContentTitle(contentTitle);
  40. builder.setLargeIcon(largeIcon);
  41. builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
  42. if (isVideoUpgradeRequest) {
  43. builder.setUsesChronometer(false);
  44. addDismissUpgradeRequestAction(builder);
  45. addAcceptUpgradeRequestAction(builder);
  46. } else {
  47. createIncomingCallNotification(call, state, builder);
  48. }
  49. addPersonReference(builder, contactInfo, call);
  50. /*
  51. * Fire off the notification // 发射通知!!
  52. */
  53. Notification notification = builder.build();
  54. Log.d(this, "Notifying IN_CALL_NOTIFICATION: " + notification);
  55. mNotificationManager.notify(IN_CALL_NOTIFICATION, notification);
  56. mIsShowingNotification = true;
  57. }
上面两个方法都要执行,一个是创建通知 一个是启动通知
1.创建通知
createIncomingCallNotification

               
  1. private void createIncomingCallNotification(
  2. Call call, int state, Notification.Builder builder) {
  3. if (state == Call.State.ACTIVE) {
  4. builder.setUsesChronometer(true);
  5. builder.setWhen(call.getConnectTimeMillis());
  6. } else {
  7. builder.setUsesChronometer(false);
  8. }

  9. // Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
  10. if (state == Call.State.ACTIVE ||
  11. state == Call.State.ONHOLD ||
  12. Call.State.isDialing(state)) {
  13. addHangupAction(builder);
  14. } else if (state == Call.State.INCOMING || state == Call.State.CALL_WAITING) {
  15. addDismissAction(builder);
  16. if (call.isVideoCall(mContext)) {
  17. addVoiceAction(builder);
  18. addVideoCallAction(builder);
  19. } else {
  20. addAnswerAction(builder);
  21. }
  22. }
  23. }
上面添加“接听”和“忽略”两个操作,

               
  1. private void addAnswerAction(Notification.Builder builder) {
  2. Log.i(this, "Will show \"answer\" action in the incoming call Notification");
  3. PendingIntent answerVoicePendingIntent = createNotificationPendingIntent(
  4. mContext, InCallApp.ACTION_ANSWER_VOICE_INCOMING_CALL);
  5. builder.addAction(R.drawable.ic_call_white_24dp,
  6. mContext.getText(R.string.description_target_answer),
  7. answerVoicePendingIntent);
  8. }
  9. private void addDismissAction(Notification.Builder builder) {
  10. Log.i(this, "Will show \"dismiss\" action in the incoming call Notification");
  11. PendingIntent declinePendingIntent =
  12. createNotificationPendingIntent(mContext, InCallApp.ACTION_DECLINE_INCOMING_CALL);
  13. builder.addAction(R.drawable.ic_close_dk,
  14. mContext.getText(R.string.notification_action_dismiss),
  15. declinePendingIntent);
  16. }
启动通知了:
NotificationManager.java

               
  1. /**
  2. * Post a notification to be shown in the status bar. If a notification with
  3. * the same id has already been posted by your application and has not yet been canceled, it
  4. * will be replaced by the updated information.
  5. *
  6. * @param id An identifier for this notification unique within your
  7. * application.
  8. * @param notification A {@link Notification} object describing what to show the user. Must not
  9. * be null.
  10. */
  11. public void notify(int id, Notification notification)
  12. {
  13. notify(null, id, notification);
  14. }

根据下面的说明我们知道,在状态栏显示处通知
我们跟的是来电的代码,来电信息呈献给用户有两种情况:
1. 屏幕唤醒状态下 是在通知栏显示通知
2. 屏幕熄灭状态下 点亮屏幕全屏显示
如下图所示:



               
  1. /**
  2. * Post a notification to be shown in the status bar. If a notification with
  3. * the same tag and id has already been posted by your application and has not yet been
  4. * canceled, it will be replaced by the updated information.
  5. *
  6. * @param tag A string identifier for this notification. May be {@code null}.
  7. * @param id An identifier for this notification. The pair (tag, id) must be unique
  8. * within your application.
  9. * @param notification A {@link Notification} object describing what to
  10. * show the user. Must not be null.
  11. */
  12. public void notify(String tag, int id, Notification notification)
  13. {
  14. int[] idOut = new int[1];
  15. INotificationManager service = getService();
  16. String pkg = mContext.getPackageName();
  17. if (notification.sound != null) {
  18. notification.sound = notification.sound.getCanonicalUri();
  19. if (StrictMode.vmFileUriExposureEnabled()) {
  20. notification.sound.checkFileUriExposed("Notification.sound");
  21. }
  22. }
  23. if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
  24. Notification stripped = notification.clone();
  25. Builder.stripForDelivery(stripped);
  26. try {
  27. service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
  28. stripped, idOut, UserHandle.myUserId());
  29. if (id != idOut[0]) {
  30. Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
  31. }
  32. } catch (RemoteException e) {
  33. }
  34. }




联发科MTK6737安卓4G全网通智能模块核心板规格书 QL160模块硬件设计指导手册深圳市褀联科技有限公司一 、综述1.1 适用范围本文档适用的产品是:QL160模块。本核心板是以MT6735(兼容MT6737/8735/6753)为平台,周围配件主要有MT6328 电源模块,存储模块,MT6625L 四合一无线模块,MT6158 和MT6169 射频模块等,通过对核心板QL160的介绍, 用以指导用户对QL160 模块进行硬件设计,并在该模块基础上... 阅读详情

相关推荐

MTK MT8678 SOC启动流程

从bb文件查找lk2源码来源,如下可以看出是从本地代码路径src/bsp/lk2获取,copy到build/tmp/work/auto2718p1_64_hyp-poky-linux/lk2/1.0/git/lk2构建。根据vendor/mediatek/proprietary/trustzone/custom/build/build_atf_image.mk。bbappend文件路径:./meta-mediatek-mt8678/recipes-bsp/lk/lk2.bbappend。

weixin_59554170的博客 1664

Android:MTK的Dialer模块联系人搜索

MTK的Dialer模块联系人搜索                    拨号搜索机制分为两个部分:引导搜索和搜索。其中引导搜索是指,从用户输入到开始搜索之间的流程,而搜索部分是指,从数据库搜索字符串的过程。 一、引导搜索部分       默认的拨号界面的布局从上到下主要分为3个部分:显示列表、数字编辑框、拨号键盘。他们的作用是:用户直接在拨号键盘上输入数字,然后数字编辑框显示所输入的数

Zed_Faker的博客 2497

L2-028 秀恩爱分得快(测试点3说明)

古人云:秀恩爱,分得快。互联网上每天都有大量人发布大量照片,我们通过分析这些照片,可以分析人与人之间的亲密度。如果一张照片上出现了 K 个人,这些人两两间的亲密度就被定义为 1/K。任意两个人如果同时出现在若干张照片里,他们之间的亲密度就是所有这些同框照片对应的亲密度之和。下面给定一批照片,请你分析一对给定的情侣,看看他们分别有没有亲密度更高的异性朋友?

daybreak_alonely的博客 1412

拨号盘搜索联系人

2,拨号盘搜索联系人 拨号盘搜索联系人流程图如下, 拨号界面对应的fragment为DialpadFragment,并且DialpadFragment实现了TextWatcher接口, afterTextChanged方法如下, if (mDialpadQueryListener != null) { mDialpadQueryListener.onDialpadQuer

Jack的博客 2712

【MCU开发规范】:车载MCU开发规范

车载MCU开发规范

1049

UE5 StateTree实战:用分层状态机重构你的AI行为逻辑(附蓝图配置避坑点)

本文详细介绍了UE5中StateTree分层状态机的实战应用,帮助开发者重构AI行为逻辑。通过核心概念解析、迁移策略、高级技巧和避坑指南,展示了StateTree在灵活性、可维护性和并行处理方面的优势,特别适合复杂AI行为的开发与优化。

weixin_42558257的博客 249

Android基础知识

具体参考Android官方简介。

jjx_fight的博客 1962

SuccessFactors入门篇

SuccessFactors入门篇—— Employee Central(一)最近通过一个月左右的学习,拿到了SF EC(SuccessFactors Employee Central )模块SAP认证,所以打算把自己学习SF的整个过程以及相关资料和大家分享一下,相互切磋,共同进步。Employee Central简单了解Employee Central简单了解新的改变功能快捷键合理的创建标题,有...

qq_42288853的博客 1万+

Spring Aop 匹配源码解析

一、前言 AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。 AOP的相关概念: (1)横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点 (2)Aspect(切面):通常是一个类,里面可以定义切入点和通知 (3)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用。被拦截到的点,因为Sp

yytree123的博客 832

一文讲透 SAP VDM 命名里的前缀与后缀:看懂 I_、C_、A_,把 CDS 视图栈搭得又稳又好用

SAP S/4HANA 的 CDS 视图命名规范解析 摘要:本文详细解析 SAP S/4HANA 中 CDS 视图的 VDM 命名规范。通过前缀(如 I_、C_、R_)区分视图层级,后缀标记附加角色,结合 @VDM.viewType 注解形成完整语义体系。I_作为稳定接口层,C_面向特定场景裁剪,R_限定应用域内复用,A_提供远程 API 接口。命名规范使团队能快速识别视图用途、依赖关系和扩展边界,确保模型长期可维护性。文章结合制造业物料主数据、Fiori 应用等实际案例,说明如何通过规范命名构建清晰的视图

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 220

2026年数字IC设计紫光展锐笔试带答案解析

本文是一份数字IC设计工程师/芯片验证工程师的笔试样题,包含单选题和多选题两部分。单选题共20题(每题2分),涵盖数字电路、C++/Verilog编程、芯片设计基础等内容,例如D/A转换计算、面向对象编程特性、三极管工作区域、Verilog仿真时间格式等。多选题15题(每题2分),考察Hold违例修复方法、验证方法学等知识点。试卷总分100分,考试时长90分钟,题型设计兼顾基础理论与工程实践,重点测试应聘者对芯片设计核心概念和工具链的掌握程度。部分题目解析详细说明了计算过程或概念原理,如第1题通过二进制转换

Open 110

Android 8.1电话拨号盘搜索源码逻辑分析

DialpadFragment.java afterTextChanged() 方法: if (mDialpadQueryListener != null) { mDialpadQueryListener.onDialpadQueryChanged(mDigits.getText().toString()); } 在onResume()中: final Dialtact...

qq_35041057的博客 1422

C++面向对象编程-继承与派生练习题

2、共同派生类 3、人员类Person的派生(虚基类的使用)

编程小鹏的博客 1191

MT8735 andorid7.0 充电调试总结(1)——MTK电源管理(MT6318)

看代码前,先看一个power surply(psy)子系统框架的东西: https://blog.csdn.net/u012830148/article/details/80227903 https://blog.csdn.net/u011483863/article/details/68486873 psy(电供设备):电池状态(温度 电量等)管理(电池库仑计的事情,向上层汇报电池状态...

c_L_j_的博客 5080

uniapp [安卓苹果App端] - 实现APP开发后台保活+息屏后台持续运行效果,强制后台保活手机黑屏后进程存活后台保持网络连接或持续定位等功能提供支持(app后台运行休眠仍然可以确保一直运行)

uniapp APP开发后台保活,Uniapp-APP后台保活插件(安卓苹果ios后台保活)保证应用在后台继续运行,uniapp实现app保活 保后台运行 避免系统主动杀进程,如何让安卓APP一直在后台运行?uniapp 安卓 APP 后台持续运行(保活)iOS App后台保活,Android 后台运行白名单实现保活,保活息屏后台运行,uniapp后台保活,uniapp熄屏 后台运行,Android/ios进程保活/息屏后后台保持定位、网络运行,uniapp在android ios保活,挂后台可运行,安卓A

🏆 前端领域优质创作者 1万+

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

android. 4.4 MT(来电)流程

先来看看MT(来电)的整个流程图: [img]http://img.blog.csdn.net/20140403182220859?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWlob25neXVlbGFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center...

iteye_1939的博客 384

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

mt7 android6.0 回退,华为mate7安卓6.0怎么回退系统

匿名用户1级2016-07-05 回答Mate7 M版本(Android6.0+EMUI4.0)可以回退到Android 5.1+EMUI 3.1官方稳定版本或Andorid 4.4+EMUI 3.0官方稳定版本。回退到L版本(Android5.1+EMUI3.1)方法如下:方法一:使用华为手机助手,无需下载中转包即可直接回退至稳定版。具体请参考:http://club.huawei.com/th...

weixin_32965939的博客 1459

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

MT8781核心板_MTK8781安卓核心板规格参数

MT8781安卓核心板以其强大的性能和高效的能耐备受瞩目。其八核CPU架构包括(2x Cortex-A76 @ 2.2GHz + 6x Cortex-A55 @ 2.0GHz),以及高性能的Arm Mali G57级GPU。同时,配备高达2,133MHz的LPDDR4X内存和快速的UFS 2.2级存储,大大加速了数据访问速度,为用户带来更加顺畅的使用体验。

ziver_sz的博客 3817

酷派android4.4.4到5.0,酷派大神F2和大神F1安卓5.0系统升级将到来

谷歌安卓5.0系统发布已经有一段时间了,但是国产品牌手机基本无余力开发移植,根据可靠消息酷派大神F2和大神F1安卓5.0班的CoolUI6刷机包将在6月左右发布。大神F1(8297)做为大神手机的开山之作,曾以颇具竞争力的价格吸引了很多网友购买,但是由于是早期产品,大神F1所搭载的是Android 4.2定制的CoolUI5.5,在Kitkat系统普及之后,因升级过慢饱受用户吐槽。于去年底,大神F...

weixin_31830691的博客 1611

MTK8781安卓核心板_MT8781(Helio G99)核心板性能参数

MT8781安卓核心板搭载了八核CPU,其中包括两个主频高达2.2GHz的高性能Arm Cortex-A76处理器。这一处理器采用了台积电6纳米级芯片生产工艺,以及先进的3D图形功能的高性能Arm Mali G57级GPU。通过超快LPDDR4X内存和UFS 2.2存储供电,不仅提高了游戏、应用程序和日常活动的性能,还加速了数据访问速度,带来更加流畅的体验。

ziver_sz的博客 7064

MT8163芯片资料简介 原理图和参考PCB设计资料

MT8163芯片资料简介 原理图和参考PCB设计资料 以下是MT8163芯片资料分享,找不到的资料的小伙伴不用着急了!!!! 联发科技MT8163这款高度整合式系统单芯片 (SoC) 囊括了各项先进的功能特色,例如四核心 ARM® Cortex-A53 MPCore™、3D 图像技术 (OpenGL|ES 3.1)、1300 万像素镜头影像信号处理器 (ISP)、DDR3/L 800 MHz ...

uuzz8888的博客 1万+

Android L Phone进程初始化_01

本文代码目录以高通平台Android 5.0为基础,可能与AOSP有不同之处。概述前一篇学习了5.0 Phone模块的目录结构以及进程模型,使得新人有个大概的了解。按讲,接下来应该是MO、MT流程分析了,这是从解决实际问题的角度来说。其实,如果说要从解决问题的角度来说,那就应该是分析InCallUI布局结构,以及Fragment, UI, Presenter他们之间的关系来分析了。

shrimpcolo的专栏 5757

MT6580A从入门到精通

MT6580A是联发科(MediaTek)于2015 年 Q1推出的一款面向入门级智能手机和 IoT 设备的系统级芯片(SoC)。它采用28nm制程工艺,集成了四核 ARM Cortex-A7 处理器、Mali-400 MP2 GPU、3G 基带、Wi-Fi/蓝牙/GPS 等多功能模块于单一芯片上。MT6580A 的标准配套电源管理 IC 是MT6328(部分设计也用 MT6323)。MT6328 功能框图│ ││ Power Input: 2.5~4.5V (电池/USB) ││ ││ │。

qq_64312451的博客 262

MT6763+Android 8.0下ST7701S屏幕驱动移植实战

MIPI DSI屏幕驱动是嵌入式Linux显示系统的核心技术环节,其本质是协调Kernel DTS配置、Panel初始化时序、DSI PHY电气参数与Android Treble HAL接口的多层协同。ST7701S作为高性价比国产MIPI桥接IC,虽支持RGB转MIPI、内置Gamma校准,但因缺乏官方HAL支持、私有寄存器(如0xB0–0xBF系列)及严苛供电/时序要求,在MT6763平台适配中极易出现黑屏、花屏、TE同步失败等问题。其技术价值在于实现低成本屏替换与产线快速导入,典型应用于中低端Andr

weixin_34037515的博客 450

MT5G芯片天玑800/MTK6873/MT6873WiFi6+Android10.0 NSA和SA双模5G核心板

MTK5G-MT6873+wifi6+android10.0 NSA和SA双模5G核心板/MTK5G方案定制 1.MT6873,八核(4xA76+4xA55)支持Sub-6GHz频段的NSA和SA双模5G 2.支持最大分辨率 FHD+(2160x1080);电容式触控屏Incell 3.WiFi 6(2x2 801.11 ax) 双天线 4.Type-C(HDMI)快充,5V2A,4.4V,OTG 5.摄像头最大支持 64M \ 32M+16M,指纹识别,nfc 6、操作系统 Android 10.0 ..

szx940213的博客 5585

MT6761芯片Android系统启动精密架构设计全过程

摘要:本文详细分析了MT6761芯片架构及Android系统从内核启动到Launcher显示的完整流程。MT6761采用四核Cortex-A53设计,搭配PowerVR GE8320 GPU,支持LPDDR4X内存和1080p视频处理。系统启动分为内核初始化(约50ms)、Init进程(430ms)、Zygote预加载(800ms)、SystemServer启动(1350ms)和Launcher显示(250ms)等阶段,涉及内存管理、进程调度、Binder通信等核心机制。在MT6761平台上,通过CPU绑定

zhilin_tang的专栏 1079
上一篇: Android 中 RegistrantList消息处理机制 以android 5.0 MT为例
下一篇: Android 5.0 Screen pinning 屏幕固定功能
十橙
博客等级 码龄24年 312粉丝 95原创
评论 14
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值