欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android6.0來電號碼與電話薄聯(lián)系人進(jìn)行匹配

 更新時(shí)間:2016年07月21日 09:00:22   作者:murphykwu  
這篇文章主要為大家詳細(xì)介紹了Android6.0來電號碼與電話薄聯(lián)系人進(jìn)行匹配的方法,感興趣的小伙伴們可以參考一下

本文將介紹系統(tǒng)接收到來電之后,如何在電話薄中進(jìn)行匹配聯(lián)系人的流程。分析將從另外一篇文章(基于Android6.0的RIL框架層模塊分析)中提到的與本文內(nèi)容相關(guān)的代碼開始。

//packages/service/***/Call.java

public void handleCreateConnectionSuccess(

 CallIdMapper idMapper,

 ParcelableConnection connection) {

 setHandle(connection.getHandle(), connection.getHandlePresentation());//這個函數(shù)很重要,會啟動一個查詢

 setCallerDisplayName(connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());

 setExtras(connection.getExtras());

 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()));

 }

}

這個setHandle函數(shù)如下:

//Call.java

public void setHandle(Uri handle, int presentation) {

 startCallerInfoLookup();

}

private void startCallerInfoLookup() {

 final String number = mHandle == null ? null : mHandle.getSchemeSpecificPart();

 mQueryToken++; // Updated so that previous queries can no longer set the information.

 mCallerInfo = null;

 if (!TextUtils.isEmpty(number)) {

 mHandler.post(new Runnable() {

  @Override

  public void run() {

  mCallerInfoAsyncQueryFactory.startQuery(mQueryToken,

   mContext,number,mCallerInfoQueryListener,Call.this);

  }});

 }

}

注意后面post的那個Runnable。這個就是啟動查詢號碼的邏輯了。這個mCallerInfoAsyncQueryFactory的賦值的流程比較曲折。在TelecomService被連接上調(diào)用onBind的時(shí)候,會調(diào)用initializeTelecomSystem函數(shù)。那這個TelecomService是在哪里被啟動的呢?在TelecomLoaderService.java里面定義了:

private static final ComponentName SERVICE_COMPONENT = new ComponentName(

  "com.android.server.telecom",

  "com.android.server.telecom.components.TelecomService");

private void connectToTelecom() {

 synchronized (mLock) {

 TelecomServiceConnection serviceConnection = new TelecomServiceConnection();

 Intent intent = new Intent(SERVICE_ACTION);

 intent.setComponent(SERVICE_COMPONENT);

 // Bind to Telecom and register the service

 if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.OWNER)) {

  mServiceConnection = serviceConnection;

 } }}

public void onBootPhase(int phase) {//這個在系統(tǒng)啟動階段就會觸發(fā)

 if (phase == PHASE_ACTIVITY_MANAGER_READY) {

 connectToTelecom();

 }}

所以從這里看,在系統(tǒng)啟動階段就會觸發(fā)TelecomService這個service,且在成功連接到服務(wù)之后,將調(diào)用ServiceManager.addService(Context.TELECOM_SERVICE, service),將這個服務(wù)添加到系統(tǒng)服務(wù)中了。這個類的構(gòu)造函數(shù)中,在調(diào)用函數(shù)initializeTelecomSystem初始化TelecomSystem時(shí),就實(shí)例化了一個內(nèi)部匿名對象,并且在TelecomSystem的構(gòu)造函數(shù)中初始化一個mCallsManager時(shí)將該匿名對象傳入,而在CallsManager的processIncomingCallIntent中會用這個函數(shù)初始化一個Call對象。所以這個mCallerInfoAsyncQueryFactory的實(shí)際內(nèi)容見TelecomService中的initializeTelecomSystem:

//TelecomService.java

TelecomSystem.setInstance(

 new TelecomSystem(

 context,

 new MissedCallNotifierImpl(context.getApplicationContext()),

 new CallerInfoAsyncQueryFactory() {

  @Override

  public CallerInfoAsyncQuery startQuery(int token, Context context,

   String number,CallerInfoAsyncQuery.OnQueryCompleteListener listener,

   Object cookie) {

  return CallerInfoAsyncQuery.startQuery(token, context, number, listener, cookie);

  }},

 new HeadsetMediaButtonFactory() {},

 new ProximitySensorManagerFactory() {},

 new InCallWakeLockControllerFactory() {},

 new ViceNotifier() {}));

可以看到,通過startQuery來查詢傳入的number的動作。我們來看看CallerInfoAsyncQuery的startQuery函數(shù)。

//frameworks/base/telephony/java/com/android/internal/CallerInfoAsyncQuery.java

/**

 * Factory method to start the query based on a number.

 *

 * Note: if the number contains an "@" character we treat it

 * as a SIP address, and look it up directly in the Data table

 * rather than using the PhoneLookup table.

 * TODO: But eventually we should expose two separate methods, one for

 * numbers and one for SIP addresses, and then have

 * PhoneUtils.startGetCallerInfo() decide which one to call based on

 * the phone type of the incoming connection.

 */

 public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,

  OnQueryCompleteListener listener, Object cookie) {

 int subId = SubscriptionManager.getDefaultSubId();

 return startQuery(token, context, number, listener, cookie, subId);

 }

/**

 * Factory method to start the query with a Uri query spec.

 */ 

public static CallerInfoAsyncQuery startQuery(int token, Context context, Uri contactRef,

  OnQueryCompleteListener listener, Object cookie) {

c.mHandler.startQuery(token,

    cw, // cookie
    contactRef, // uri,注意這里的查詢地址
    null, // projection
    null, // selection
    null, // selectionArgs
    null); // orderBy
 return c;
}

注意看注釋,該函數(shù)還會對SIP號碼(包含@的號碼)進(jìn)行處理,還有緊急號碼和語音郵箱號碼進(jìn)行區(qū)分。實(shí)際上,當(dāng)對一個號碼進(jìn)行查詢的時(shí)候,這三個startQuery都用到了。注意,上面的startQuery會根據(jù)結(jié)果對connection的值進(jìn)行修改。

其中將號碼轉(zhuǎn)換成uri格式的數(shù)據(jù),后續(xù)會對這個數(shù)據(jù)進(jìn)行查詢:

//frameworks/base/***/CallerInfoAsyncQuery.java
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number, OnQueryCompleteListener listener, Object cookie, int subId) {
 // Construct the URI object and query params, and start the query.
 final Uri contactRef = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon().appendPath(number)
  .appendQueryParameter(PhoneLookup.QUERY_PARAMETER_SIP_ADDRESS, String.valueOf(PhoneNumberUtils.isUriNumber(number)))
  .build();
 CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
 c.allocate(context, contactRef);
 //create cookieWrapper, start query
 CookieWrapper cw = new CookieWrapper();
 cw.listener = listener; cw.cookie = cookie;
 cw.number = number; cw.subId = subId;
 // check to see if these are recognized numbers, and use shortcuts if we can.
 if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
 cw.event = EVENT_EMERGENCY_NUMBER;
 } else if (PhoneNumberUtils.isVoiceMailNumber(subId, number)) {
 cw.event = EVENT_VOICEMAIL_NUMBER;
 } else {
 cw.event = EVENT_NEW_QUERY;
 }

 c.mHandler.startQuery(token,
    cw, // cookie
    contactRef, // uri
    null, // projection
    null, // selection
    null, // selectionArgs
    null); // orderBy
 return c;
}

這個函數(shù)里面的contactRef的值應(yīng)該是“content://com.android.contacts/phone_lookup_enterprise/13678909678/sip?”類似的。

實(shí)際上這個query是調(diào)用CallerInfoAsyncQueryHandler的startQuery函數(shù),而這個函數(shù)是直接調(diào)用它的父類AsyncQueryHandler的同名函數(shù)。

//AsyncQueryHandler.java
public void startQuery(int token, Object cookie, Uri uri,
 String[] projection, String selection, String[] selectionArgs,
 String orderBy) {
 // Use the token as what so cancelOperations works properly
 Message msg = mWorkerThreadHandler.obtainMessage(token);
 msg.arg1 = EVENT_ARG_QUERY;
 WorkerArgs args = new WorkerArgs();
 args.handler = this;
 args.uri = uri;
 msg.obj = args;
 mWorkerThreadHandler.sendMessage(msg);

}

這個mWorkerThreadHandler是在CallerInfoAsyncQueryHandler函數(shù)覆寫父類的createHandler函數(shù)中賦值,是CallerInfoWorkerHandler類型。所以后續(xù)的處理函數(shù)是該類的handleMessage函數(shù)。

//AsyncQueryHandler.java
public void handleMessage(Message msg) {
 WorkerArgs args = (WorkerArgs) msg.obj;
 CookieWrapper cw = (CookieWrapper) args.cookie;
 if (cw == null) {
 // Normally, this should never be the case for calls originating
 // from within this code.
 // However, if there is any code that this Handler calls (such as in
 // super.handleMessage) that DOES place unexpected messages on the
 // queue, then we need pass these messages on.
 } else {
 switch (cw.event) {
  case EVENT_NEW_QUERY://它的值跟AsyncQueryHandler的EVENT_ARG_QUERY一樣,都是1
  //start the sql command.
  super.handleMessage(msg);
  break;
  case EVENT_END_OF_QUEUE:
  // query was already completed, so just send the reply.
  // passing the original token value back to the caller
  // on top of the event values in arg1.
  Message reply = args.handler.obtainMessage(msg.what);
  reply.obj = args;
  reply.arg1 = msg.arg1;
  reply.sendToTarget();
  break;
  default:
 }}}}

這個super就是AsyncQueryHandler的內(nèi)部類WorkerHandler了。

//AsyncQueryHandler.java
protected class WorkerHandler extends Handler {
 @Override
 public void handleMessage(Message msg) {
 final ContentResolver resolver = mResolver.get();
 WorkerArgs args = (WorkerArgs) msg.obj;
 int token = msg.what;
 int event = msg.arg1;
 switch (event) {
  case EVENT_ARG_QUERY:
  Cursor cursor;
  try {
   cursor = resolver.query(args.uri, args.projection,
    args.selection, args.selectionArgs,
    args.orderBy);
   // Calling getCount() causes the cursor window to be filled,
   // which will make the first access on the main thread a lot faster.
   if (cursor != null) {
   cursor.getCount();
   }} 
  args.result = cursor;
  break;
 }
 // passing the original token value back to the caller
 // on top of the event values in arg1.
 Message reply = args.handler.obtainMessage(token);

 reply.obj = args;
 reply.arg1 = msg.arg1;
 reply.sendToTarget();
 }}

可以看到流程就是簡單的用resolver.query來查詢指定的query URI,然后將返回值通過消息機(jī)制發(fā)送到AsyncQueryHandler的handleMessage里面處理,而在這里會調(diào)用CallerInfoAsyncQuery的onQueryComplete函數(shù)。注意這個ContentResolver是在uri上查詢結(jié)果,而這個uri是由某個ContentProvider來提供的。注意這個地址里面的authorities里面的值為”com.android.contacts”,同樣看看ContactsProvider的androidmanifest.xml文件:

<provider android:name="ContactsProvider2"
  android:authorities="contacts;com.android.contacts"
  android:readPermission="android.permission.READ_CONTACTS"
  android:writePermission="android.permission.WRITE_CONTACTS">
  <path-permission android:pathPrefix="/search_suggest_query"
   android:readPermission="android.permission.GLOBAL_SEARCH" />
  <path-permission android:pathPattern="/contacts/.*/photo"   android:readPermission="android.permission.GLOBAL_SEARCH" />
  <grant-uri-permission android:pathPattern=".*" />
 </provider>

所以最后這個查詢是由ContactsProvider來執(zhí)行的。

我們來看看查詢完成之后,調(diào)用CallerInfoAsyncQuery的onQueryComplete函數(shù)的具體流程:

protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
 // check the token and if needed, create the callerinfo object.
 if (mCallerInfo == null) {
  if (cw.event == EVENT_EMERGENCY_NUMBER) {
  } else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
  } else {
  mCallerInfo = CallerInfo.getCallerInfo(mContext, mQueryUri, cursor);
  }
  }
 }
 //notify the listener that the query is complete.
 if (cw.listener != null) {
  cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
 }
 }
}

注意,上面代碼里面的CallerInfo.getCallerInfo非常重要。在這里面會使用查詢處理的cursor結(jié)果,并將合適的結(jié)果填充到mCallerInfo,將其傳遞到cw.listener.onQueryComplete函數(shù)中,作為最終結(jié)果進(jìn)行進(jìn)一步處理。

//CallerInfo.java
public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
 CallerInfo info = new CallerInfo();
 if (cursor != null) {
 if (cursor.moveToFirst()) {
  columnIndex = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY);
  if (columnIndex != -1) {
  info.lookupKey = cursor.getString(columnIndex);

  }
  info.contactExists = true;
 }
 cursor.close();
 cursor = null;
 }
 info.needUpdate = false;
 info.name = normalize(info.name);
 info.contactRefUri = contactRef;
 return info;
}

系統(tǒng)原生的邏輯是取搜索結(jié)果的第一個記錄,并用來實(shí)例化。當(dāng)客戶需求改變,需要匹配不同號碼的時(shí)候,就需要修改這個地方的了。最優(yōu)先是遍歷整個cursor集合,并且根據(jù)客戶需求選出適合的結(jié)果,賦值給CallerInfo實(shí)例。

下面是整個號碼匹配的流程圖:


Call.java會將查詢后的結(jié)果設(shè)置到Call實(shí)例里面,并將其傳送到CallsManager里面進(jìn)行后續(xù)處理。而這個CallsManager會將這個Call顯示給客戶。

當(dāng)網(wǎng)絡(luò)端來電時(shí),frame層會接收到,并且連接成功之后會觸發(fā)Call.java里面的handleCreateConnectionSuccess。這個函數(shù)邏輯是從數(shù)據(jù)庫中查詢復(fù)合要求的聯(lián)系人,并且只取結(jié)果集的第一條記錄,用來初始化這個Call里面的變量。而后將這個Call傳到CallsManager進(jìn)行處理,顯示給用戶。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論