1/* 2 * Copyright (C) 2011 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 17package com.android.nfc; 18 19import android.bluetooth.BluetoothAdapter; 20 21import com.android.nfc.RegisteredComponentCache.ComponentInfo; 22import com.android.nfc.handover.HandoverDataParser; 23import com.android.nfc.handover.PeripheralHandoverService; 24 25import android.app.Activity; 26import android.app.ActivityManager; 27import android.app.ActivityManagerNative; 28import android.app.IActivityManager; 29import android.app.PendingIntent; 30import android.app.PendingIntent.CanceledException; 31import android.content.ComponentName; 32import android.content.ContentResolver; 33import android.content.Context; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.content.pm.PackageManager; 37import android.content.pm.PackageManager.NameNotFoundException; 38import android.content.pm.ResolveInfo; 39import android.content.res.Resources.NotFoundException; 40import android.net.Uri; 41import android.nfc.NdefMessage; 42import android.nfc.NdefRecord; 43import android.nfc.NfcAdapter; 44import android.nfc.Tag; 45import android.nfc.tech.Ndef; 46import android.nfc.tech.NfcBarcode; 47import android.os.RemoteException; 48import android.os.UserHandle; 49import android.util.Log; 50 51import java.io.FileDescriptor; 52import java.io.PrintWriter; 53import java.nio.charset.StandardCharsets; 54import java.util.ArrayList; 55import java.util.Arrays; 56import java.util.LinkedList; 57import java.util.List; 58 59/** 60 * Dispatch of NFC events to start activities 61 */ 62class NfcDispatcher { 63 private static final boolean DBG = false; 64 private static final String TAG = "NfcDispatcher"; 65 66 static final int DISPATCH_SUCCESS = 1; 67 static final int DISPATCH_FAIL = 2; 68 static final int DISPATCH_UNLOCK = 3; 69 70 private final Context mContext; 71 private final IActivityManager mIActivityManager; 72 private final RegisteredComponentCache mTechListFilters; 73 private final ContentResolver mContentResolver; 74 private final HandoverDataParser mHandoverDataParser; 75 private final String[] mProvisioningMimes; 76 private final ScreenStateHelper mScreenStateHelper; 77 private final NfcUnlockManager mNfcUnlockManager; 78 private final boolean mDeviceSupportsBluetooth; 79 80 // Locked on this 81 private PendingIntent mOverrideIntent; 82 private IntentFilter[] mOverrideFilters; 83 private String[][] mOverrideTechLists; 84 private boolean mProvisioningOnly; 85 86 NfcDispatcher(Context context, 87 HandoverDataParser handoverDataParser, 88 boolean provisionOnly) { 89 mContext = context; 90 mIActivityManager = ActivityManagerNative.getDefault(); 91 mTechListFilters = new RegisteredComponentCache(mContext, 92 NfcAdapter.ACTION_TECH_DISCOVERED, NfcAdapter.ACTION_TECH_DISCOVERED); 93 mContentResolver = context.getContentResolver(); 94 mHandoverDataParser = handoverDataParser; 95 mScreenStateHelper = new ScreenStateHelper(context); 96 mNfcUnlockManager = NfcUnlockManager.getInstance(); 97 mDeviceSupportsBluetooth = BluetoothAdapter.getDefaultAdapter() != null; 98 99 synchronized (this) { 100 mProvisioningOnly = provisionOnly; 101 } 102 String[] provisionMimes = null; 103 if (provisionOnly) { 104 try { 105 // Get accepted mime-types 106 provisionMimes = context.getResources(). 107 getStringArray(R.array.provisioning_mime_types); 108 } catch (NotFoundException e) { 109 provisionMimes = null; 110 } 111 } 112 mProvisioningMimes = provisionMimes; 113 } 114 115 public synchronized void setForegroundDispatch(PendingIntent intent, 116 IntentFilter[] filters, String[][] techLists) { 117 if (DBG) Log.d(TAG, "Set Foreground Dispatch"); 118 mOverrideIntent = intent; 119 mOverrideFilters = filters; 120 mOverrideTechLists = techLists; 121 } 122 123 public synchronized void disableProvisioningMode() { 124 mProvisioningOnly = false; 125 } 126 127 /** 128 * Helper for re-used objects and methods during a single tag dispatch. 129 */ 130 static class DispatchInfo { 131 public final Intent intent; 132 133 final Intent rootIntent; 134 final Uri ndefUri; 135 final String ndefMimeType; 136 final PackageManager packageManager; 137 final Context context; 138 139 public DispatchInfo(Context context, Tag tag, NdefMessage message) { 140 intent = new Intent(); 141 intent.putExtra(NfcAdapter.EXTRA_TAG, tag); 142 intent.putExtra(NfcAdapter.EXTRA_ID, tag.getId()); 143 if (message != null) { 144 intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] {message}); 145 ndefUri = message.getRecords()[0].toUri(); 146 ndefMimeType = message.getRecords()[0].toMimeType(); 147 } else { 148 ndefUri = null; 149 ndefMimeType = null; 150 } 151 152 rootIntent = new Intent(context, NfcRootActivity.class); 153 rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intent); 154 rootIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 155 156 this.context = context; 157 packageManager = context.getPackageManager(); 158 } 159 160 public Intent setNdefIntent() { 161 intent.setAction(NfcAdapter.ACTION_NDEF_DISCOVERED); 162 if (ndefUri != null) { 163 intent.setData(ndefUri); 164 return intent; 165 } else if (ndefMimeType != null) { 166 intent.setType(ndefMimeType); 167 return intent; 168 } 169 return null; 170 } 171 172 public Intent setTechIntent() { 173 intent.setData(null); 174 intent.setType(null); 175 intent.setAction(NfcAdapter.ACTION_TECH_DISCOVERED); 176 return intent; 177 } 178 179 public Intent setTagIntent() { 180 intent.setData(null); 181 intent.setType(null); 182 intent.setAction(NfcAdapter.ACTION_TAG_DISCOVERED); 183 return intent; 184 } 185 186 /** 187 * Launch the activity via a (single) NFC root task, so that it 188 * creates a new task stack instead of interfering with any existing 189 * task stack for that activity. 190 * NfcRootActivity acts as the task root, it immediately calls 191 * start activity on the intent it is passed. 192 */ 193 boolean tryStartActivity() { 194 // Ideally we'd have used startActivityForResult() to determine whether the 195 // NfcRootActivity was able to launch the intent, but startActivityForResult() 196 // is not available on Context. Instead, we query the PackageManager beforehand 197 // to determine if there is an Activity to handle this intent, and base the 198 // result of off that. 199 List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0, 200 ActivityManager.getCurrentUser()); 201 if (activities.size() > 0) { 202 context.startActivityAsUser(rootIntent, UserHandle.CURRENT); 203 return true; 204 } 205 return false; 206 } 207 208 boolean tryStartActivity(Intent intentToStart) { 209 List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser( 210 intentToStart, 0, ActivityManager.getCurrentUser()); 211 if (activities.size() > 0) { 212 rootIntent.putExtra(NfcRootActivity.EXTRA_LAUNCH_INTENT, intentToStart); 213 context.startActivityAsUser(rootIntent, UserHandle.CURRENT); 214 return true; 215 } 216 return false; 217 } 218 } 219 220 /** Returns: 221 * <ul> 222 * <li /> DISPATCH_SUCCESS if dispatched to an activity, 223 * <li /> DISPATCH_FAIL if no activities were found to dispatch to, 224 * <li /> DISPATCH_UNLOCK if the tag was used to unlock the device 225 * </ul> 226 */ 227 public int dispatchTag(Tag tag) { 228 PendingIntent overrideIntent; 229 IntentFilter[] overrideFilters; 230 String[][] overrideTechLists; 231 boolean provisioningOnly; 232 233 synchronized (this) { 234 overrideFilters = mOverrideFilters; 235 overrideIntent = mOverrideIntent; 236 overrideTechLists = mOverrideTechLists; 237 provisioningOnly = mProvisioningOnly; 238 } 239 240 boolean screenUnlocked = false; 241 if (!provisioningOnly && 242 mScreenStateHelper.checkScreenState() == ScreenStateHelper.SCREEN_STATE_ON_LOCKED) { 243 screenUnlocked = handleNfcUnlock(tag); 244 if (!screenUnlocked) { 245 return DISPATCH_FAIL; 246 } 247 } 248 249 NdefMessage message = null; 250 Ndef ndef = Ndef.get(tag); 251 if (ndef != null) { 252 message = ndef.getCachedNdefMessage(); 253 } else { 254 NfcBarcode nfcBarcode = NfcBarcode.get(tag); 255 if (nfcBarcode != null && nfcBarcode.getType() == NfcBarcode.TYPE_KOVIO) { 256 message = decodeNfcBarcodeUri(nfcBarcode); 257 } 258 } 259 260 if (DBG) Log.d(TAG, "dispatch tag: " + tag.toString() + " message: " + message); 261 262 DispatchInfo dispatch = new DispatchInfo(mContext, tag, message); 263 264 resumeAppSwitches(); 265 266 if (tryOverrides(dispatch, tag, message, overrideIntent, overrideFilters, 267 overrideTechLists)) { 268 return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 269 } 270 271 if (tryPeripheralHandover(message)) { 272 if (DBG) Log.i(TAG, "matched BT HANDOVER"); 273 return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 274 } 275 276 if (NfcWifiProtectedSetup.tryNfcWifiSetup(ndef, mContext)) { 277 if (DBG) Log.i(TAG, "matched NFC WPS TOKEN"); 278 return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 279 } 280 281 if (tryNdef(dispatch, message, provisioningOnly)) { 282 return screenUnlocked ? DISPATCH_UNLOCK : DISPATCH_SUCCESS; 283 } 284 285 if (screenUnlocked) { 286 // We only allow NDEF-based mimeType matching in case of an unlock 287 return DISPATCH_UNLOCK; 288 } 289 290 if (provisioningOnly) { 291 // We only allow NDEF-based mimeType matching 292 return DISPATCH_FAIL; 293 } 294 295 // Only allow NDEF-based mimeType matching for unlock tags 296 if (tryTech(dispatch, tag)) { 297 return DISPATCH_SUCCESS; 298 } 299 300 dispatch.setTagIntent(); 301 if (dispatch.tryStartActivity()) { 302 if (DBG) Log.i(TAG, "matched TAG"); 303 return DISPATCH_SUCCESS; 304 } 305 306 if (DBG) Log.i(TAG, "no match"); 307 return DISPATCH_FAIL; 308 } 309 310 private boolean handleNfcUnlock(Tag tag) { 311 return mNfcUnlockManager.tryUnlock(tag); 312 } 313 314 /** 315 * Checks for the presence of a URL stored in a tag with tech NfcBarcode. 316 * If found, decodes URL and returns NdefMessage message containing an 317 * NdefRecord containing the decoded URL. If not found, returns null. 318 * 319 * URLs are decoded as follows: 320 * 321 * Ignore first byte (which is 0x80 ORd with a manufacturer ID, corresponding 322 * to ISO/IEC 7816-6). 323 * The second byte describes the payload data format. There are four defined data 324 * format values that identify URL data. Depending on the data format value, the 325 * associated prefix is appended to the URL data: 326 * 327 * 0x01: URL with "http://www." prefix 328 * 0x02: URL with "https://www." prefix 329 * 0x03: URL with "http://" prefix 330 * 0x04: URL with "https://" prefix 331 * 332 * Other data format values do not identify URL data and are not handled by this function. 333 * URL payload is encoded in US-ASCII, following the limitations defined in RFC3987. 334 * see http://www.ietf.org/rfc/rfc3987.txt 335 * 336 * The final two bytes of a tag with tech NfcBarcode are always reserved for CRC data, 337 * and are therefore not part of the payload. They are ignored in the decoding of a URL. 338 * 339 * The default assumption is that the URL occupies the entire payload of the NfcBarcode 340 * ID and all bytes of the NfcBarcode payload are decoded until the CRC (final two bytes) 341 * is reached. However, the OPTIONAL early terminator byte 0xfe can be used to signal 342 * an early end of the URL. Once this function reaches an early terminator byte 0xfe, 343 * URL decoding stops and the NdefMessage is created and returned. Any payload data after 344 * the first early terminator byte is ignored for the purposes of URL decoding. 345 */ 346 private NdefMessage decodeNfcBarcodeUri(NfcBarcode nfcBarcode) { 347 final byte URI_PREFIX_HTTP_WWW = (byte) 0x01; // "http://www." 348 final byte URI_PREFIX_HTTPS_WWW = (byte) 0x02; // "https://www." 349 final byte URI_PREFIX_HTTP = (byte) 0x03; // "http://" 350 final byte URI_PREFIX_HTTPS = (byte) 0x04; // "https://" 351 352 NdefMessage message = null; 353 byte[] tagId = nfcBarcode.getTag().getId(); 354 // All tags of NfcBarcode technology and Kovio type have lengths of a multiple of 16 bytes 355 if (tagId.length >= 4 356 && (tagId[1] == URI_PREFIX_HTTP_WWW || tagId[1] == URI_PREFIX_HTTPS_WWW 357 || tagId[1] == URI_PREFIX_HTTP || tagId[1] == URI_PREFIX_HTTPS)) { 358 // Look for optional URI terminator (0xfe), used to indicate the end of a URI prior to 359 // the end of the full NfcBarcode payload. No terminator means that the URI occupies the 360 // entire length of the payload field. Exclude checking the CRC in the final two bytes 361 // of the NfcBarcode tagId. 362 int end = 2; 363 for (; end < tagId.length - 2; end++) { 364 if (tagId[end] == (byte) 0xfe) { 365 break; 366 } 367 } 368 byte[] payload = new byte[end - 1]; // Skip also first byte (manufacturer ID) 369 System.arraycopy(tagId, 1, payload, 0, payload.length); 370 NdefRecord uriRecord = new NdefRecord( 371 NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, tagId, payload); 372 message = new NdefMessage(uriRecord); 373 } 374 return message; 375 } 376 377 boolean tryOverrides(DispatchInfo dispatch, Tag tag, NdefMessage message, PendingIntent overrideIntent, 378 IntentFilter[] overrideFilters, String[][] overrideTechLists) { 379 if (overrideIntent == null) { 380 return false; 381 } 382 Intent intent; 383 384 // NDEF 385 if (message != null) { 386 intent = dispatch.setNdefIntent(); 387 if (intent != null && 388 isFilterMatch(intent, overrideFilters, overrideTechLists != null)) { 389 try { 390 overrideIntent.send(mContext, Activity.RESULT_OK, intent); 391 if (DBG) Log.i(TAG, "matched NDEF override"); 392 return true; 393 } catch (CanceledException e) { 394 return false; 395 } 396 } 397 } 398 399 // TECH 400 intent = dispatch.setTechIntent(); 401 if (isTechMatch(tag, overrideTechLists)) { 402 try { 403 overrideIntent.send(mContext, Activity.RESULT_OK, intent); 404 if (DBG) Log.i(TAG, "matched TECH override"); 405 return true; 406 } catch (CanceledException e) { 407 return false; 408 } 409 } 410 411 // TAG 412 intent = dispatch.setTagIntent(); 413 if (isFilterMatch(intent, overrideFilters, overrideTechLists != null)) { 414 try { 415 overrideIntent.send(mContext, Activity.RESULT_OK, intent); 416 if (DBG) Log.i(TAG, "matched TAG override"); 417 return true; 418 } catch (CanceledException e) { 419 return false; 420 } 421 } 422 return false; 423 } 424 425 boolean isFilterMatch(Intent intent, IntentFilter[] filters, boolean hasTechFilter) { 426 if (filters != null) { 427 for (IntentFilter filter : filters) { 428 if (filter.match(mContentResolver, intent, false, TAG) >= 0) { 429 return true; 430 } 431 } 432 } else if (!hasTechFilter) { 433 return true; // always match if both filters and techlists are null 434 } 435 return false; 436 } 437 438 boolean isTechMatch(Tag tag, String[][] techLists) { 439 if (techLists == null) { 440 return false; 441 } 442 443 String[] tagTechs = tag.getTechList(); 444 Arrays.sort(tagTechs); 445 for (String[] filterTechs : techLists) { 446 if (filterMatch(tagTechs, filterTechs)) { 447 return true; 448 } 449 } 450 return false; 451 } 452 453 boolean tryNdef(DispatchInfo dispatch, NdefMessage message, boolean provisioningOnly) { 454 if (message == null) { 455 return false; 456 } 457 Intent intent = dispatch.setNdefIntent(); 458 459 // Bail out if the intent does not contain filterable NDEF data 460 if (intent == null) return false; 461 462 if (provisioningOnly) { 463 if (mProvisioningMimes == null || 464 !(Arrays.asList(mProvisioningMimes).contains(intent.getType()))) { 465 Log.e(TAG, "Dropping NFC intent in provisioning mode."); 466 return false; 467 } 468 } 469 470 // Try to start AAR activity with matching filter 471 List<String> aarPackages = extractAarPackages(message); 472 for (String pkg : aarPackages) { 473 dispatch.intent.setPackage(pkg); 474 if (dispatch.tryStartActivity()) { 475 if (DBG) Log.i(TAG, "matched AAR to NDEF"); 476 return true; 477 } 478 } 479 480 // Try to perform regular launch of the first AAR 481 if (aarPackages.size() > 0) { 482 String firstPackage = aarPackages.get(0); 483 PackageManager pm; 484 try { 485 UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); 486 pm = mContext.createPackageContextAsUser("android", 0, 487 currentUser).getPackageManager(); 488 } catch (NameNotFoundException e) { 489 Log.e(TAG, "Could not create user package context"); 490 return false; 491 } 492 Intent appLaunchIntent = pm.getLaunchIntentForPackage(firstPackage); 493 if (appLaunchIntent != null && dispatch.tryStartActivity(appLaunchIntent)) { 494 if (DBG) Log.i(TAG, "matched AAR to application launch"); 495 return true; 496 } 497 // Find the package in Market: 498 Intent marketIntent = getAppSearchIntent(firstPackage); 499 if (marketIntent != null && dispatch.tryStartActivity(marketIntent)) { 500 if (DBG) Log.i(TAG, "matched AAR to market launch"); 501 return true; 502 } 503 } 504 505 // regular launch 506 dispatch.intent.setPackage(null); 507 if (dispatch.tryStartActivity()) { 508 if (DBG) Log.i(TAG, "matched NDEF"); 509 return true; 510 } 511 512 return false; 513 } 514 515 static List<String> extractAarPackages(NdefMessage message) { 516 List<String> aarPackages = new LinkedList<String>(); 517 for (NdefRecord record : message.getRecords()) { 518 String pkg = checkForAar(record); 519 if (pkg != null) { 520 aarPackages.add(pkg); 521 } 522 } 523 return aarPackages; 524 } 525 526 boolean tryTech(DispatchInfo dispatch, Tag tag) { 527 dispatch.setTechIntent(); 528 529 String[] tagTechs = tag.getTechList(); 530 Arrays.sort(tagTechs); 531 532 // Standard tech dispatch path 533 ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>(); 534 List<ComponentInfo> registered = mTechListFilters.getComponents(); 535 536 PackageManager pm; 537 try { 538 UserHandle currentUser = new UserHandle(ActivityManager.getCurrentUser()); 539 pm = mContext.createPackageContextAsUser("android", 0, 540 currentUser).getPackageManager(); 541 } catch (NameNotFoundException e) { 542 Log.e(TAG, "Could not create user package context"); 543 return false; 544 } 545 // Check each registered activity to see if it matches 546 for (ComponentInfo info : registered) { 547 // Don't allow wild card matching 548 if (filterMatch(tagTechs, info.techs) && 549 isComponentEnabled(pm, info.resolveInfo)) { 550 // Add the activity as a match if it's not already in the list 551 if (!matches.contains(info.resolveInfo)) { 552 matches.add(info.resolveInfo); 553 } 554 } 555 } 556 557 if (matches.size() == 1) { 558 // Single match, launch directly 559 ResolveInfo info = matches.get(0); 560 dispatch.intent.setClassName(info.activityInfo.packageName, info.activityInfo.name); 561 if (dispatch.tryStartActivity()) { 562 if (DBG) Log.i(TAG, "matched single TECH"); 563 return true; 564 } 565 dispatch.intent.setComponent(null); 566 } else if (matches.size() > 1) { 567 // Multiple matches, show a custom activity chooser dialog 568 Intent intent = new Intent(mContext, TechListChooserActivity.class); 569 intent.putExtra(Intent.EXTRA_INTENT, dispatch.intent); 570 intent.putParcelableArrayListExtra(TechListChooserActivity.EXTRA_RESOLVE_INFOS, 571 matches); 572 if (dispatch.tryStartActivity(intent)) { 573 if (DBG) Log.i(TAG, "matched multiple TECH"); 574 return true; 575 } 576 } 577 return false; 578 } 579 580 public boolean tryPeripheralHandover(NdefMessage m) { 581 if (m == null || !mDeviceSupportsBluetooth) return false; 582 583 if (DBG) Log.d(TAG, "tryHandover(): " + m.toString()); 584 585 HandoverDataParser.BluetoothHandoverData handover = mHandoverDataParser.parseBluetooth(m); 586 if (handover == null || !handover.valid) return false; 587 588 Intent intent = new Intent(mContext, PeripheralHandoverService.class); 589 intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_DEVICE, handover.device); 590 intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_NAME, handover.name); 591 intent.putExtra(PeripheralHandoverService.EXTRA_PERIPHERAL_TRANSPORT, handover.transport); 592 mContext.startServiceAsUser(intent, UserHandle.CURRENT); 593 594 return true; 595 } 596 597 598 /** 599 * Tells the ActivityManager to resume allowing app switches. 600 * 601 * If the current app called stopAppSwitches() then our startActivity() can 602 * be delayed for several seconds. This happens with the default home 603 * screen. As a system service we can override this behavior with 604 * resumeAppSwitches(). 605 */ 606 void resumeAppSwitches() { 607 try { 608 mIActivityManager.resumeAppSwitches(); 609 } catch (RemoteException e) { } 610 } 611 612 /** Returns true if the tech list filter matches the techs on the tag */ 613 boolean filterMatch(String[] tagTechs, String[] filterTechs) { 614 if (filterTechs == null || filterTechs.length == 0) return false; 615 616 for (String tech : filterTechs) { 617 if (Arrays.binarySearch(tagTechs, tech) < 0) { 618 return false; 619 } 620 } 621 return true; 622 } 623 624 static String checkForAar(NdefRecord record) { 625 if (record.getTnf() == NdefRecord.TNF_EXTERNAL_TYPE && 626 Arrays.equals(record.getType(), NdefRecord.RTD_ANDROID_APP)) { 627 return new String(record.getPayload(), StandardCharsets.US_ASCII); 628 } 629 return null; 630 } 631 632 /** 633 * Returns an intent that can be used to find an application not currently 634 * installed on the device. 635 */ 636 static Intent getAppSearchIntent(String pkg) { 637 Intent market = new Intent(Intent.ACTION_VIEW); 638 market.setData(Uri.parse("market://details?id=" + pkg)); 639 return market; 640 } 641 642 static boolean isComponentEnabled(PackageManager pm, ResolveInfo info) { 643 boolean enabled = false; 644 ComponentName compname = new ComponentName( 645 info.activityInfo.packageName, info.activityInfo.name); 646 try { 647 // Note that getActivityInfo() will internally call 648 // isEnabledLP() to determine whether the component 649 // enabled. If it's not, null is returned. 650 if (pm.getActivityInfo(compname,0) != null) { 651 enabled = true; 652 } 653 } catch (PackageManager.NameNotFoundException e) { 654 enabled = false; 655 } 656 if (!enabled) { 657 Log.d(TAG, "Component not enabled: " + compname); 658 } 659 return enabled; 660 } 661 662 void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 663 synchronized (this) { 664 pw.println("mOverrideIntent=" + mOverrideIntent); 665 pw.println("mOverrideFilters=" + mOverrideFilters); 666 pw.println("mOverrideTechLists=" + mOverrideTechLists); 667 } 668 } 669} 670