1 /* 2 * Copyright 2014, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom.ui; 18 19 import static android.Manifest.permission.READ_PHONE_STATE; 20 21 import android.content.ComponentName; 22 import android.content.ContentProvider; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.telecom.PhoneAccountHandle; 25 import android.telecom.TelecomManager; 26 27 import com.android.server.telecom.Call; 28 import com.android.server.telecom.CallState; 29 import com.android.server.telecom.CallerInfoAsyncQueryFactory; 30 import com.android.server.telecom.CallsManager; 31 import com.android.server.telecom.CallsManagerListenerBase; 32 import com.android.server.telecom.Constants; 33 import com.android.server.telecom.ContactsAsyncHelper; 34 import com.android.server.telecom.Log; 35 import com.android.server.telecom.MissedCallNotifier; 36 import com.android.server.telecom.PhoneAccountRegistrar; 37 import com.android.server.telecom.PhoneNumberUtilsAdapter; 38 import com.android.server.telecom.R; 39 import com.android.server.telecom.Runnable; 40 import com.android.server.telecom.TelecomBroadcastIntentProcessor; 41 import com.android.server.telecom.TelecomSystem; 42 import com.android.server.telecom.components.TelecomBroadcastReceiver; 43 44 import android.app.Notification; 45 import android.app.NotificationManager; 46 import android.app.PendingIntent; 47 import android.app.TaskStackBuilder; 48 import android.content.AsyncQueryHandler; 49 import android.content.ContentValues; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.pm.ResolveInfo; 53 import android.database.Cursor; 54 import android.graphics.Bitmap; 55 import android.graphics.drawable.BitmapDrawable; 56 import android.graphics.drawable.Drawable; 57 import android.net.Uri; 58 import android.os.AsyncTask; 59 import android.os.Binder; 60 import android.os.UserHandle; 61 import android.provider.CallLog.Calls; 62 import android.telecom.DefaultDialerManager; 63 import android.telecom.DisconnectCause; 64 import android.telecom.PhoneAccount; 65 import android.telephony.PhoneNumberUtils; 66 import android.telephony.TelephonyManager; 67 import android.text.BidiFormatter; 68 import android.text.TextDirectionHeuristics; 69 import android.text.TextUtils; 70 71 import com.android.internal.telephony.CallerInfo; 72 73 import java.lang.Override; 74 import java.lang.String; 75 import java.util.List; 76 import java.util.Locale; 77 import java.util.concurrent.ConcurrentHashMap; 78 import java.util.concurrent.ConcurrentMap; 79 import java.util.concurrent.atomic.AtomicInteger; 80 81 // TODO: Needed for move to system service: import com.android.internal.R; 82 83 /** 84 * Creates a notification for calls that the user missed (neither answered nor rejected). 85 * 86 * TODO: Make TelephonyManager.clearMissedCalls call into this class. 87 * 88 * TODO: Reduce dependencies in this implementation; remove the need to create a new Call 89 * simply to look up caller metadata, and if possible, make it unnecessary to get a 90 * direct reference to the CallsManager. Try to make this class simply handle the UI 91 * and Android-framework entanglements of missed call notification. 92 */ 93 public class MissedCallNotifierImpl extends CallsManagerListenerBase implements MissedCallNotifier { 94 95 public interface MissedCallNotifierImplFactory { makeMissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter)96 MissedCallNotifier makeMissedCallNotifierImpl(Context context, 97 PhoneAccountRegistrar phoneAccountRegistrar, 98 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter); 99 } 100 101 public interface NotificationBuilderFactory { getBuilder(Context context)102 Notification.Builder getBuilder(Context context); 103 } 104 105 private static class DefaultNotificationBuilderFactory implements NotificationBuilderFactory { DefaultNotificationBuilderFactory()106 public DefaultNotificationBuilderFactory() {} 107 108 @Override getBuilder(Context context)109 public Notification.Builder getBuilder(Context context) { 110 return new Notification.Builder(context); 111 } 112 } 113 114 private static final String[] CALL_LOG_PROJECTION = new String[] { 115 Calls._ID, 116 Calls.NUMBER, 117 Calls.NUMBER_PRESENTATION, 118 Calls.DATE, 119 Calls.DURATION, 120 Calls.TYPE, 121 }; 122 123 private static final int CALL_LOG_COLUMN_ID = 0; 124 private static final int CALL_LOG_COLUMN_NUMBER = 1; 125 private static final int CALL_LOG_COLUMN_NUMBER_PRESENTATION = 2; 126 private static final int CALL_LOG_COLUMN_DATE = 3; 127 private static final int CALL_LOG_COLUMN_DURATION = 4; 128 private static final int CALL_LOG_COLUMN_TYPE = 5; 129 130 private static final int MISSED_CALL_NOTIFICATION_ID = 1; 131 132 private final Context mContext; 133 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 134 private final NotificationManager mNotificationManager; 135 private final NotificationBuilderFactory mNotificationBuilderFactory; 136 private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; 137 private UserHandle mCurrentUserHandle; 138 139 // Used to track the number of missed calls. 140 private ConcurrentMap<UserHandle, AtomicInteger> mMissedCallCounts; 141 MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter)142 public MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar, 143 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) { 144 this(context, phoneAccountRegistrar, phoneNumberUtilsAdapter, 145 new DefaultNotificationBuilderFactory()); 146 } 147 MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, NotificationBuilderFactory notificationBuilderFactory)148 public MissedCallNotifierImpl(Context context, 149 PhoneAccountRegistrar phoneAccountRegistrar, 150 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 151 NotificationBuilderFactory notificationBuilderFactory) { 152 mContext = context; 153 mPhoneAccountRegistrar = phoneAccountRegistrar; 154 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; 155 mNotificationManager = 156 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 157 158 mNotificationBuilderFactory = notificationBuilderFactory; 159 mMissedCallCounts = new ConcurrentHashMap<>(); 160 } 161 162 /** Clears missed call notification and marks the call log's missed calls as read. */ 163 @Override clearMissedCalls(UserHandle userHandle)164 public void clearMissedCalls(UserHandle userHandle) { 165 // If the default dialer is showing the missed call notification then it will modify the 166 // call log and we don't have to do anything here. 167 if (!shouldManageNotificationThroughDefaultDialer(userHandle)) { 168 markMissedCallsAsRead(userHandle); 169 } 170 cancelMissedCallNotification(userHandle); 171 } 172 markMissedCallsAsRead(final UserHandle userHandle)173 private void markMissedCallsAsRead(final UserHandle userHandle) { 174 AsyncTask.execute(new Runnable("MCNI.mMCAR", null /*lock*/) { 175 @Override 176 public void loggedRun() { 177 // Clear the list of new missed calls from the call log. 178 ContentValues values = new ContentValues(); 179 values.put(Calls.NEW, 0); 180 values.put(Calls.IS_READ, 1); 181 StringBuilder where = new StringBuilder(); 182 where.append(Calls.NEW); 183 where.append(" = 1 AND "); 184 where.append(Calls.TYPE); 185 where.append(" = ?"); 186 try { 187 Uri callsUri = ContentProvider 188 .maybeAddUserId(Calls.CONTENT_URI, userHandle.getIdentifier()); 189 mContext.getContentResolver().update(callsUri, values, 190 where.toString(), new String[]{ Integer.toString(Calls. 191 MISSED_TYPE) }); 192 } catch (IllegalArgumentException e) { 193 Log.w(this, "ContactsProvider update command failed", e); 194 } 195 } 196 }.prepare()); 197 } 198 199 /** 200 * Returns the missed-call notificatino intent to send to the default dialer for the given user. * Note, the passed in userHandle is always the non-managed user for SIM calls (multi-user 201 * calls). In this case we return the default dialer for the logged in user. This is never the 202 * managed (work profile) dialer. 203 * 204 * For non-multi-user calls (3rd party phone accounts), the passed in userHandle is the user 205 * handle of the phone account. This could be a managed user. In that case we return the default 206 * dialer for the given user which could be a managed (work profile) dialer. 207 */ getShowMissedCallIntentForDefaultDialer(UserHandle userHandle)208 private Intent getShowMissedCallIntentForDefaultDialer(UserHandle userHandle) { 209 String dialerPackage = DefaultDialerManager 210 .getDefaultDialerApplication(mContext, userHandle.getIdentifier()); 211 if (TextUtils.isEmpty(dialerPackage)) { 212 return null; 213 } 214 return new Intent(TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION) 215 .setPackage(dialerPackage); 216 } 217 shouldManageNotificationThroughDefaultDialer(UserHandle userHandle)218 private boolean shouldManageNotificationThroughDefaultDialer(UserHandle userHandle) { 219 Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle); 220 if (intent == null) { 221 return false; 222 } 223 224 List<ResolveInfo> receivers = mContext.getPackageManager() 225 .queryBroadcastReceiversAsUser(intent, 0, userHandle.getIdentifier()); 226 return receivers.size() > 0; 227 } 228 sendNotificationThroughDefaultDialer(Call call, UserHandle userHandle)229 private void sendNotificationThroughDefaultDialer(Call call, UserHandle userHandle) { 230 int count = mMissedCallCounts.get(userHandle).get(); 231 Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle) 232 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND) 233 .putExtra(TelecomManager.EXTRA_CLEAR_MISSED_CALLS_INTENT, 234 createClearMissedCallsPendingIntent(userHandle)) 235 .putExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, count) 236 .putExtra(TelecomManager.EXTRA_NOTIFICATION_PHONE_NUMBER, 237 call != null ? call.getPhoneNumber() : null); 238 239 if (count == 1 && call != null) { 240 final Uri handleUri = call.getHandle(); 241 String handle = handleUri == null ? null : handleUri.getSchemeSpecificPart(); 242 243 if (!TextUtils.isEmpty(handle) && !TextUtils.equals(handle, 244 mContext.getString(R.string.handle_restricted))) { 245 intent.putExtra(TelecomManager.EXTRA_CALL_BACK_INTENT, 246 createCallBackPendingIntent(handleUri, userHandle)); 247 } 248 } 249 250 251 Log.w(this, "Showing missed calls through default dialer."); 252 mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE); 253 } 254 255 /** 256 * Create a system notification for the missed call. 257 * 258 * @param call The missed call. 259 */ 260 @Override showMissedCallNotification(Call call)261 public void showMissedCallNotification(Call call) { 262 final PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount(); 263 final PhoneAccount phoneAccount = 264 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle); 265 UserHandle userHandle; 266 if (phoneAccount != null && 267 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 268 userHandle = mCurrentUserHandle; 269 } else { 270 userHandle = phoneAccountHandle.getUserHandle(); 271 } 272 showMissedCallNotification(call, userHandle); 273 } 274 showMissedCallNotification(Call call, UserHandle userHandle)275 private void showMissedCallNotification(Call call, UserHandle userHandle) { 276 mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0)); 277 int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet(); 278 279 if (shouldManageNotificationThroughDefaultDialer(userHandle)) { 280 sendNotificationThroughDefaultDialer(call, userHandle); 281 return; 282 } 283 284 final int titleResId; 285 final String expandedText; // The text in the notification's line 1 and 2. 286 287 // Display the first line of the notification: 288 // 1 missed call: <caller name || handle> 289 // More than 1 missed call: <number of calls> + "missed calls" 290 if (missCallCounts == 1) { 291 expandedText = getNameForCall(call); 292 293 CallerInfo ci = call.getCallerInfo(); 294 if (ci != null && ci.userType == CallerInfo.USER_TYPE_WORK) { 295 titleResId = R.string.notification_missedWorkCallTitle; 296 } else { 297 titleResId = R.string.notification_missedCallTitle; 298 } 299 } else { 300 titleResId = R.string.notification_missedCallsTitle; 301 expandedText = 302 mContext.getString(R.string.notification_missedCallsMsg, missCallCounts); 303 } 304 305 // Create a public viewable version of the notification, suitable for display when sensitive 306 // notification content is hidden. 307 // We use user's context here to make sure notification is badged if it is a managed user. 308 Context contextForUser = getContextForUser(userHandle); 309 Notification.Builder publicBuilder = mNotificationBuilderFactory.getBuilder(contextForUser); 310 publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call) 311 .setColor(mContext.getResources().getColor(R.color.theme_color)) 312 .setWhen(call.getCreationTimeMillis()) 313 // Show "Phone" for notification title. 314 .setContentTitle(mContext.getText(R.string.userCallActivityLabel)) 315 // Notification details shows that there are missed call(s), but does not reveal 316 // the missed caller information. 317 .setContentText(mContext.getText(titleResId)) 318 .setContentIntent(createCallLogPendingIntent(userHandle)) 319 .setAutoCancel(true) 320 .setDeleteIntent(createClearMissedCallsPendingIntent(userHandle)); 321 322 // Create the notification suitable for display when sensitive information is showing. 323 Notification.Builder builder = mNotificationBuilderFactory.getBuilder(contextForUser); 324 builder.setSmallIcon(android.R.drawable.stat_notify_missed_call) 325 .setColor(mContext.getResources().getColor(R.color.theme_color)) 326 .setWhen(call.getCreationTimeMillis()) 327 .setContentTitle(mContext.getText(titleResId)) 328 .setContentText(expandedText) 329 .setContentIntent(createCallLogPendingIntent(userHandle)) 330 .setAutoCancel(true) 331 .setDeleteIntent(createClearMissedCallsPendingIntent(userHandle)) 332 // Include a public version of the notification to be shown when the missed call 333 // notification is shown on the user's lock screen and they have chosen to hide 334 // sensitive notification information. 335 .setPublicVersion(publicBuilder.build()); 336 337 Uri handleUri = call.getHandle(); 338 String handle = handleUri == null ? null : handleUri.getSchemeSpecificPart(); 339 340 // Add additional actions when there is only 1 missed call, like call-back and SMS. 341 if (missCallCounts == 1) { 342 Log.d(this, "Add actions with number %s.", Log.piiHandle(handle)); 343 344 if (!TextUtils.isEmpty(handle) 345 && !TextUtils.equals(handle, mContext.getString(R.string.handle_restricted))) { 346 builder.addAction(R.drawable.ic_phone_24dp, 347 mContext.getString(R.string.notification_missedCall_call_back), 348 createCallBackPendingIntent(handleUri, userHandle)); 349 350 if (canRespondViaSms(call)) { 351 builder.addAction(R.drawable.ic_message_24dp, 352 mContext.getString(R.string.notification_missedCall_message), 353 createSendSmsFromNotificationPendingIntent(handleUri, userHandle)); 354 } 355 } 356 357 Bitmap photoIcon = call.getPhotoIcon(); 358 if (photoIcon != null) { 359 builder.setLargeIcon(photoIcon); 360 } else { 361 Drawable photo = call.getPhoto(); 362 if (photo != null && photo instanceof BitmapDrawable) { 363 builder.setLargeIcon(((BitmapDrawable) photo).getBitmap()); 364 } 365 } 366 } else { 367 Log.d(this, "Suppress actions. handle: %s, missedCalls: %d.", Log.piiHandle(handle), 368 missCallCounts); 369 } 370 371 Notification notification = builder.build(); 372 configureLedOnNotification(notification); 373 374 Log.i(this, "Adding missed call notification for %s.", call); 375 long token = Binder.clearCallingIdentity(); 376 try { 377 mNotificationManager.notifyAsUser( 378 null /* tag */, MISSED_CALL_NOTIFICATION_ID, notification, userHandle); 379 } finally { 380 Binder.restoreCallingIdentity(token); 381 } 382 } 383 384 385 /** Cancels the "missed call" notification. */ cancelMissedCallNotification(UserHandle userHandle)386 private void cancelMissedCallNotification(UserHandle userHandle) { 387 // Reset the number of missed calls to 0. 388 mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0)); 389 mMissedCallCounts.get(userHandle).set(0); 390 391 if (shouldManageNotificationThroughDefaultDialer(userHandle)) { 392 sendNotificationThroughDefaultDialer(null, userHandle); 393 return; 394 } 395 396 long token = Binder.clearCallingIdentity(); 397 try { 398 mNotificationManager.cancelAsUser(null, MISSED_CALL_NOTIFICATION_ID, userHandle); 399 } finally { 400 Binder.restoreCallingIdentity(token); 401 } 402 } 403 404 /** 405 * Returns the name to use in the missed call notification. 406 */ getNameForCall(Call call)407 private String getNameForCall(Call call) { 408 String handle = call.getHandle() == null ? null : call.getHandle().getSchemeSpecificPart(); 409 String name = call.getName(); 410 411 if (!TextUtils.isEmpty(handle)) { 412 String formattedNumber = PhoneNumberUtils.formatNumber(handle, 413 getCurrentCountryIso(mContext)); 414 415 // The formatted number will be null if there was a problem formatting it, but we can 416 // default to using the unformatted number instead (e.g. a SIP URI may not be able to 417 // be formatted. 418 if (!TextUtils.isEmpty(formattedNumber)) { 419 handle = formattedNumber; 420 } 421 } 422 423 if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) { 424 return name; 425 } else if (!TextUtils.isEmpty(handle)) { 426 // A handle should always be displayed LTR using {@link BidiFormatter} regardless of the 427 // content of the rest of the notification. 428 // TODO: Does this apply to SIP addresses? 429 BidiFormatter bidiFormatter = BidiFormatter.getInstance(); 430 return bidiFormatter.unicodeWrap(handle, TextDirectionHeuristics.LTR); 431 } else { 432 // Use "unknown" if the call is unidentifiable. 433 return mContext.getString(R.string.unknown); 434 } 435 } 436 437 /** 438 * @return The ISO 3166-1 two letters country code of the country the user is in based on the 439 * network location. If the network location does not exist, fall back to the locale 440 * setting. 441 */ getCurrentCountryIso(Context context)442 private String getCurrentCountryIso(Context context) { 443 // Without framework function calls, this seems to be the most accurate location service 444 // we can rely on. 445 final TelephonyManager telephonyManager = 446 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 447 String countryIso = telephonyManager.getNetworkCountryIso().toUpperCase(); 448 449 if (countryIso == null) { 450 countryIso = Locale.getDefault().getCountry(); 451 Log.w(this, "No CountryDetector; falling back to countryIso based on locale: " 452 + countryIso); 453 } 454 return countryIso; 455 } 456 457 /** 458 * Creates a new pending intent that sends the user to the call log. 459 * 460 * @return The pending intent. 461 */ createCallLogPendingIntent(UserHandle userHandle)462 private PendingIntent createCallLogPendingIntent(UserHandle userHandle) { 463 Intent intent = new Intent(Intent.ACTION_VIEW, null); 464 intent.setType(Calls.CONTENT_TYPE); 465 466 TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(mContext); 467 taskStackBuilder.addNextIntent(intent); 468 469 return taskStackBuilder.getPendingIntent(0, 0, null, userHandle); 470 } 471 472 /** 473 * Creates an intent to be invoked when the missed call notification is cleared. 474 */ createClearMissedCallsPendingIntent(UserHandle userHandle)475 private PendingIntent createClearMissedCallsPendingIntent(UserHandle userHandle) { 476 return createTelecomPendingIntent( 477 TelecomBroadcastIntentProcessor.ACTION_CLEAR_MISSED_CALLS, null, userHandle); 478 } 479 480 /** 481 * Creates an intent to be invoked when the user opts to "call back" from the missed call 482 * notification. 483 * 484 * @param handle The handle to call back. 485 */ createCallBackPendingIntent(Uri handle, UserHandle userHandle)486 private PendingIntent createCallBackPendingIntent(Uri handle, UserHandle userHandle) { 487 return createTelecomPendingIntent( 488 TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION, handle, 489 userHandle); 490 } 491 492 /** 493 * Creates an intent to be invoked when the user opts to "send sms" from the missed call 494 * notification. 495 */ createSendSmsFromNotificationPendingIntent(Uri handle, UserHandle userHandle)496 private PendingIntent createSendSmsFromNotificationPendingIntent(Uri handle, 497 UserHandle userHandle) { 498 return createTelecomPendingIntent( 499 TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION, 500 Uri.fromParts(Constants.SCHEME_SMSTO, handle.getSchemeSpecificPart(), null), 501 userHandle); 502 } 503 504 /** 505 * Creates generic pending intent from the specified parameters to be received by 506 * {@link TelecomBroadcastIntentProcessor}. 507 * 508 * @param action The intent action. 509 * @param data The intent data. 510 */ createTelecomPendingIntent(String action, Uri data, UserHandle userHandle)511 private PendingIntent createTelecomPendingIntent(String action, Uri data, 512 UserHandle userHandle) { 513 Intent intent = new Intent(action, data, mContext, TelecomBroadcastReceiver.class); 514 intent.putExtra(TelecomBroadcastIntentProcessor.EXTRA_USERHANDLE, userHandle); 515 return PendingIntent.getBroadcast(mContext, 0, intent, 0); 516 } 517 518 /** 519 * Configures a notification to emit the blinky notification light. 520 */ configureLedOnNotification(Notification notification)521 private void configureLedOnNotification(Notification notification) { 522 notification.flags |= Notification.FLAG_SHOW_LIGHTS; 523 notification.defaults |= Notification.DEFAULT_LIGHTS; 524 } 525 canRespondViaSms(Call call)526 private boolean canRespondViaSms(Call call) { 527 // Only allow respond-via-sms for "tel:" calls. 528 return call.getHandle() != null && 529 PhoneAccount.SCHEME_TEL.equals(call.getHandle().getScheme()); 530 } 531 532 /** 533 * Adds the missed call notification on startup if there are unread missed calls. 534 */ 535 @Override reloadFromDatabase( final TelecomSystem.SyncRoot lock, final CallsManager callsManager, final ContactsAsyncHelper contactsAsyncHelper, final CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, final UserHandle userHandle)536 public void reloadFromDatabase( 537 final TelecomSystem.SyncRoot lock, 538 final CallsManager callsManager, 539 final ContactsAsyncHelper contactsAsyncHelper, 540 final CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, 541 final UserHandle userHandle) { 542 Log.d(this, "reloadFromDatabase()..."); 543 544 // instantiate query handler 545 AsyncQueryHandler queryHandler = new AsyncQueryHandler(mContext.getContentResolver()) { 546 @Override 547 protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 548 Log.d(MissedCallNotifierImpl.this, "onQueryComplete()..."); 549 if (cursor != null) { 550 try { 551 mMissedCallCounts.remove(userHandle); 552 while (cursor.moveToNext()) { 553 // Get data about the missed call from the cursor 554 final String handleString = cursor.getString(CALL_LOG_COLUMN_NUMBER); 555 final int presentation = 556 cursor.getInt(CALL_LOG_COLUMN_NUMBER_PRESENTATION); 557 final long date = cursor.getLong(CALL_LOG_COLUMN_DATE); 558 559 final Uri handle; 560 if (presentation != Calls.PRESENTATION_ALLOWED 561 || TextUtils.isEmpty(handleString)) { 562 handle = null; 563 } else { 564 handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(handleString) ? 565 PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL, 566 handleString, null); 567 } 568 569 synchronized (lock) { 570 571 // Convert the data to a call object 572 Call call = new Call(Call.CALL_ID_UNKNOWN, mContext, callsManager, 573 lock, null, contactsAsyncHelper, 574 callerInfoAsyncQueryFactory, mPhoneNumberUtilsAdapter, null, 575 null, null, null, Call.CALL_DIRECTION_INCOMING, false, 576 false); 577 call.setDisconnectCause( 578 new DisconnectCause(DisconnectCause.MISSED)); 579 call.setState(CallState.DISCONNECTED, "throw away call"); 580 call.setCreationTimeMillis(date); 581 582 // Listen for the update to the caller information before posting 583 // the notification so that we have the contact info and photo. 584 call.addListener(new Call.ListenerBase() { 585 @Override 586 public void onCallerInfoChanged(Call call) { 587 call.removeListener( 588 this); // No longer need to listen to call 589 // changes after the contact info 590 // is retrieved. 591 showMissedCallNotification(call, userHandle); 592 } 593 }); 594 // Set the handle here because that is what triggers the contact 595 // info query. 596 call.setHandle(handle, presentation); 597 } 598 } 599 } finally { 600 cursor.close(); 601 } 602 } 603 } 604 }; 605 606 // setup query spec, look for all Missed calls that are new. 607 StringBuilder where = new StringBuilder("type="); 608 where.append(Calls.MISSED_TYPE); 609 where.append(" AND new=1"); 610 where.append(" AND is_read=0"); 611 612 Uri callsUri = 613 ContentProvider.maybeAddUserId(Calls.CONTENT_URI, userHandle.getIdentifier()); 614 // start the query 615 queryHandler.startQuery(0, null, callsUri, CALL_LOG_PROJECTION, 616 where.toString(), null, Calls.DEFAULT_SORT_ORDER); 617 } 618 619 @Override setCurrentUserHandle(UserHandle currentUserHandle)620 public void setCurrentUserHandle(UserHandle currentUserHandle) { 621 mCurrentUserHandle = currentUserHandle; 622 } 623 getContextForUser(UserHandle user)624 private Context getContextForUser(UserHandle user) { 625 try { 626 return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user); 627 } catch (NameNotFoundException e) { 628 // Default to mContext, not finding the package system is running as is unlikely. 629 return mContext; 630 } 631 } 632 } 633