CallerInfoUtils.java revision 789d810ee67f42b9ce7062423552f317af59fa78
17ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Leepackage com.android.incallui; 27ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 37ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Leeimport android.content.Context; 4bf41b978dcb99db47247db6c56821d8090131f6eYorke Leeimport android.content.Loader; 5bf41b978dcb99db47247db6c56821d8090131f6eYorke Leeimport android.content.Loader.OnLoadCompleteListener; 6fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Leeimport android.net.Uri; 76cddf46812634fadc194830774110780f14e9462Tyler Gunnimport android.telecom.PhoneAccount; 86cddf46812634fadc194830774110780f14e9462Tyler Gunnimport android.telecom.TelecomManager; 97ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Leeimport android.text.TextUtils; 10bf41b978dcb99db47247db6c56821d8090131f6eYorke Leeimport android.util.Log; 11bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee 12bf41b978dcb99db47247db6c56821d8090131f6eYorke Leeimport com.android.contacts.common.model.Contact; 13bf41b978dcb99db47247db6c56821d8090131f6eYorke Leeimport com.android.contacts.common.model.ContactLoader; 147ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 157ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Leeimport java.util.Arrays; 167ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 177ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee/** 18fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee * Utility methods for contact and caller info related functionality 197ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee */ 207ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Leepublic class CallerInfoUtils { 217ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 227ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee private static final String TAG = CallerInfoUtils.class.getSimpleName(); 237ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 247ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee /** Define for not a special CNAP string */ 257ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee private static final int CNAP_SPECIAL_CASE_NO = -1; 267ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 277ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee public CallerInfoUtils() { 287ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 297ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 307ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee private static final int QUERY_TOKEN = -1; 317ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 327ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee /** 334bbe4c6900dfe3584f674161dbe15e248d5adbe7Santos Cordon * This is called to get caller info for a call. This will return a CallerInfo 344bbe4c6900dfe3584f674161dbe15e248d5adbe7Santos Cordon * object immediately based off information in the call, but 357ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * more information is returned to the OnQueryCompleteListener (which contains 367ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * information about the phone number label, user's name, etc). 377ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee */ 38fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal public static CallerInfo getCallerInfoForCall(Context context, Call call, 397ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee CallerInfoAsyncQuery.OnQueryCompleteListener listener) { 408b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng CallerInfo info = buildCallerInfo(context, call); 417ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 428b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng // TODO: Have phoneapp send a Uri when it knows the contact that triggered this call. 438b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 446cddf46812634fadc194830774110780f14e9462Tyler Gunn if (info.numberPresentation == TelecomManager.PRESENTATION_ALLOWED) { 458b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng // Start the query with the number provided from the call. 468b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng Log.d(TAG, "==> Actually starting CallerInfoAsyncQuery.startQuery()..."); 475ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context, info, listener, call); 488b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 498b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng return info; 508b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng } 518b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 52fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal public static CallerInfo buildCallerInfo(Context context, Call call) { 538b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng CallerInfo info = new CallerInfo(); 548b6c8d0dbfba6eabe3d835f8cdcec8a13e253d0cChiao Cheng 557ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // Store CNAP information retrieved from the Connection (we want to do this 567ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // here regardless of whether the number is empty or not). 57fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal info.cnapName = call.getCnapName(); 587ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee info.name = info.cnapName; 59fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal info.numberPresentation = call.getNumberPresentation(); 60fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal info.namePresentation = call.getCnapNamePresentation(); 617ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 62fc22ba88566ef70e202128335231c367de6c52afSailesh Nepal String number = call.getNumber(); 634bbe4c6900dfe3584f674161dbe15e248d5adbe7Santos Cordon if (!TextUtils.isEmpty(number)) { 64101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner final String[] numbers = number.split("&"); 65101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner number = numbers[0]; 66101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner if (numbers.length > 1) { 67101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner info.forwardingNumber = numbers[1]; 68101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner } 69101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner 704bbe4c6900dfe3584f674161dbe15e248d5adbe7Santos Cordon number = modifyForSpecialCnapCases(context, info, number, info.numberPresentation); 714bbe4c6900dfe3584f674161dbe15e248d5adbe7Santos Cordon info.phoneNumber = number; 727ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 735ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen 745ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen // Because the InCallUI is immediately launched before the call is connected, occasionally 755ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen // a voicemail call will be passed to InCallUI as a "voicemail:" URI without a number. 765ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen // This call should still be handled as a voicemail call. 7704318a393b5492fb72d6edafca588e6b25ad752cNancy Chen if ((call.getHandle() != null && 7804318a393b5492fb72d6edafca588e6b25ad752cNancy Chen PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())) || 7904318a393b5492fb72d6edafca588e6b25ad752cNancy Chen isVoiceMailNumber(context, call)) { 805ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen info.markAsVoiceMail(context); 815ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen } 825ea0dcd39ff1f29d09d7bb146d7eb20f24b32201Nancy Chen 83789d810ee67f42b9ce7062423552f317af59fa78Yorke Lee ContactInfoCache.getInstance(context).maybeInsertCnapInformationIntoCache(context, call, 84789d810ee67f42b9ce7062423552f317af59fa78Yorke Lee info); 85789d810ee67f42b9ce7062423552f317af59fa78Yorke Lee 867ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee return info; 877ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 887ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 8904318a393b5492fb72d6edafca588e6b25ad752cNancy Chen public static boolean isVoiceMailNumber(Context context, Call call) { 9004318a393b5492fb72d6edafca588e6b25ad752cNancy Chen TelecomManager telecomManager = 9104318a393b5492fb72d6edafca588e6b25ad752cNancy Chen (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); 9204318a393b5492fb72d6edafca588e6b25ad752cNancy Chen return telecomManager.isVoiceMailNumber( 9304318a393b5492fb72d6edafca588e6b25ad752cNancy Chen call.getTelecommCall().getDetails().getAccountHandle(), call.getNumber()); 9404318a393b5492fb72d6edafca588e6b25ad752cNancy Chen } 9504318a393b5492fb72d6edafca588e6b25ad752cNancy Chen 967ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee /** 977ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * Handles certain "corner cases" for CNAP. When we receive weird phone numbers 987ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * from the network to indicate different number presentations, convert them to 997ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * expected number and presentation values within the CallerInfo object. 1007ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * @param number number we use to verify if we are in a corner case 1017ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * @param presentation presentation value used to verify if we are in a corner case 1027ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee * @return the new String that should be used for the phone number 1037ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee */ 1047ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee /* package */static String modifyForSpecialCnapCases(Context context, CallerInfo ci, 1055461c6a3b5090e6fab47076c5c0aeb2d1091606dSailesh Nepal String number, int presentation) { 1067ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // Obviously we return number if ci == null, but still return number if 1077ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // number == null, because in these cases the correct string will still be 1087ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // displayed/logged after this function returns based on the presentation value. 1097ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee if (ci == null || number == null) return number; 1107ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1111a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(TAG, "modifyForSpecialCnapCases: initially, number=" 1127ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee + toLogSafePhoneNumber(number) 1137ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee + ", presentation=" + presentation + " ci " + ci); 1147ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1157ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // "ABSENT NUMBER" is a possible value we could get from the network as the 1167ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // phone number, so if this happens, change it to "Unknown" in the CallerInfo 1177ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // and fix the presentation to be the same. 1187ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee final String[] absentNumberValues = 1197ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee context.getResources().getStringArray(R.array.absent_num); 1207ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee if (Arrays.asList(absentNumberValues).contains(number) 1216cddf46812634fadc194830774110780f14e9462Tyler Gunn && presentation == TelecomManager.PRESENTATION_ALLOWED) { 1227ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee number = context.getString(R.string.unknown); 1236cddf46812634fadc194830774110780f14e9462Tyler Gunn ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN; 1247ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1257ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1267ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // Check for other special "corner cases" for CNAP and fix them similarly. Corner 1277ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // cases only apply if we received an allowed presentation from the network, so check 1287ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't 1297ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // match the presentation passed in for verification (meaning we changed it previously 1307ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // because it's a corner case and we're being called from a different entry point). 1316cddf46812634fadc194830774110780f14e9462Tyler Gunn if (ci.numberPresentation == TelecomManager.PRESENTATION_ALLOWED 1327ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee || (ci.numberPresentation != presentation 1336cddf46812634fadc194830774110780f14e9462Tyler Gunn && presentation == TelecomManager.PRESENTATION_ALLOWED)) { 13407a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal // For all special strings, change number & numberPrentation. 13507a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal if (isCnapSpecialCaseRestricted(number)) { 13607a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal number = context.getString(R.string.private_num); 1376cddf46812634fadc194830774110780f14e9462Tyler Gunn ci.numberPresentation = TelecomManager.PRESENTATION_RESTRICTED; 13807a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal } else if (isCnapSpecialCaseUnknown(number)) { 13907a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal number = context.getString(R.string.unknown); 1406cddf46812634fadc194830774110780f14e9462Tyler Gunn ci.numberPresentation = TelecomManager.PRESENTATION_UNKNOWN; 1417ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 14207a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal Log.d(TAG, "SpecialCnap: number=" + toLogSafePhoneNumber(number) 14307a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal + "; presentation now=" + ci.numberPresentation); 1447ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1451a7f2bcab2d2023f2ee4cfb0bc57bc265b5aab87Chiao Cheng Log.d(TAG, "modifyForSpecialCnapCases: returning number string=" 1467ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee + toLogSafePhoneNumber(number)); 1477ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee return number; 1487ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1497ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 15007a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal private static boolean isCnapSpecialCaseRestricted(String n) { 15107a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal return n.equals("PRIVATE") || n.equals("P") || n.equals("RES"); 15207a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal } 15307a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal 15407a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal private static boolean isCnapSpecialCaseUnknown(String n) { 15507a822b2ea92dc5b68cd43139be7efe4c6640cd4Sailesh Nepal return n.equals("UNAVAILABLE") || n.equals("UNKNOWN") || n.equals("UNA") || n.equals("U"); 1567ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1577ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1587ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee /* package */static String toLogSafePhoneNumber(String number) { 1597ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // For unknown number, log empty string. 1607ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee if (number == null) { 1617ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee return ""; 1627ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1637ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1642d84705bf4c4f0588ccf4a6d64832e7f99b532cfChristine Chen // Todo: Figure out an equivalent for VDBG 1657ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee if (false) { 1667ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // When VDBG is true we emit PII. 1677ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee return number; 1687ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1697ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee 1707ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare 1717ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee // sanitized phone numbers. 1727ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee StringBuilder builder = new StringBuilder(); 1737ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee for (int i = 0; i < number.length(); i++) { 1747ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee char c = number.charAt(i); 175101bed44998ff35c3a56f431345fac5c8229ec0eJay Shrauner if (c == '-' || c == '@' || c == '.' || c == '&') { 1767ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee builder.append(c); 1777ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } else { 1787ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee builder.append('x'); 1797ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1807ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 1817ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee return builder.toString(); 1827ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee } 183fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee 184fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee /** 185bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee * Send a notification using a {@link ContactLoader} to inform the sync adapter that we are 186bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee * viewing a particular contact, so that it can download the high-res photo. 187fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee */ 188fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee public static void sendViewNotification(Context context, Uri contactUri) { 189bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee final ContactLoader loader = new ContactLoader(context, contactUri, 190bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee true /* postViewNotification */); 191bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee loader.registerListener(0, new OnLoadCompleteListener<Contact>() { 192bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee @Override 193bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee public void onLoadComplete( 194bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee Loader<Contact> loader, Contact contact) { 195bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee try { 196bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee loader.reset(); 197bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee } catch (RuntimeException e) { 198bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee Log.e(TAG, "Error resetting loader", e); 199bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee } 200bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee } 201bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee }); 202bf41b978dcb99db47247db6c56821d8090131f6eYorke Lee loader.startLoading(); 203fd76bfcd5bd3ee6cc1eb53019a23738192972180Yorke Lee } 2047ef707319c0e3563ff92a772ba53cb5dca0e3ae1Yorke Lee} 205