1 /* 2 * Copyright (C) 2013 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 android.nfc.cardemulation; 18 19 import android.annotation.Nullable; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.content.ComponentName; 22 import android.content.pm.PackageManager; 23 import android.content.pm.PackageManager.NameNotFoundException; 24 import android.content.pm.ResolveInfo; 25 import android.content.pm.ServiceInfo; 26 import android.content.res.Resources; 27 import android.content.res.Resources.NotFoundException; 28 import android.content.res.TypedArray; 29 import android.content.res.XmlResourceParser; 30 import android.graphics.drawable.Drawable; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.util.AttributeSet; 34 import android.util.Log; 35 import android.util.Xml; 36 import android.util.proto.ProtoOutputStream; 37 38 import org.xmlpull.v1.XmlPullParser; 39 import org.xmlpull.v1.XmlPullParserException; 40 41 import java.io.FileDescriptor; 42 import java.io.IOException; 43 import java.io.PrintWriter; 44 import java.util.ArrayList; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Map; 48 49 /** 50 * @hide 51 */ 52 public final class ApduServiceInfo implements Parcelable { 53 static final String TAG = "ApduServiceInfo"; 54 55 /** 56 * The service that implements this 57 */ 58 @UnsupportedAppUsage 59 final ResolveInfo mService; 60 61 /** 62 * Description of the service 63 */ 64 final String mDescription; 65 66 /** 67 * Whether this service represents AIDs running on the host CPU 68 */ 69 final boolean mOnHost; 70 71 /** 72 * Offhost reader name. 73 * eg: SIM, eSE etc 74 */ 75 String mOffHostName; 76 77 /** 78 * Offhost reader name from manifest file. 79 * Used for unsetOffHostSecureElement() 80 */ 81 final String mStaticOffHostName; 82 83 /** 84 * Mapping from category to static AID group 85 */ 86 @UnsupportedAppUsage 87 final HashMap<String, AidGroup> mStaticAidGroups; 88 89 /** 90 * Mapping from category to dynamic AID group 91 */ 92 @UnsupportedAppUsage 93 final HashMap<String, AidGroup> mDynamicAidGroups; 94 95 /** 96 * Whether this service should only be started when the device is unlocked. 97 */ 98 final boolean mRequiresDeviceUnlock; 99 100 /** 101 * Whether this service should only be started when the device is screen on. 102 */ 103 final boolean mRequiresDeviceScreenOn; 104 105 /** 106 * The id of the service banner specified in XML. 107 */ 108 final int mBannerResourceId; 109 110 /** 111 * The uid of the package the service belongs to 112 */ 113 final int mUid; 114 115 /** 116 * Settings Activity for this service 117 */ 118 final String mSettingsActivityName; 119 120 /** 121 * @hide 122 */ 123 @UnsupportedAppUsage ApduServiceInfo(ResolveInfo info, boolean onHost, String description, ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, boolean requiresUnlock, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost)124 public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, 125 ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, 126 boolean requiresUnlock, int bannerResource, int uid, 127 String settingsActivityName, String offHost, String staticOffHost) { 128 this(info, onHost, description, staticAidGroups, dynamicAidGroups, 129 requiresUnlock, onHost ? true : false, bannerResource, uid, 130 settingsActivityName, offHost, staticOffHost); 131 } 132 133 /** 134 * @hide 135 */ ApduServiceInfo(ResolveInfo info, boolean onHost, String description, ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, String settingsActivityName, String offHost, String staticOffHost)136 public ApduServiceInfo(ResolveInfo info, boolean onHost, String description, 137 ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups, 138 boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid, 139 String settingsActivityName, String offHost, String staticOffHost) { 140 this.mService = info; 141 this.mDescription = description; 142 this.mStaticAidGroups = new HashMap<String, AidGroup>(); 143 this.mDynamicAidGroups = new HashMap<String, AidGroup>(); 144 this.mOffHostName = offHost; 145 this.mStaticOffHostName = staticOffHost; 146 this.mOnHost = onHost; 147 this.mRequiresDeviceUnlock = requiresUnlock; 148 this.mRequiresDeviceScreenOn = requiresScreenOn; 149 for (AidGroup aidGroup : staticAidGroups) { 150 this.mStaticAidGroups.put(aidGroup.category, aidGroup); 151 } 152 for (AidGroup aidGroup : dynamicAidGroups) { 153 this.mDynamicAidGroups.put(aidGroup.category, aidGroup); 154 } 155 this.mBannerResourceId = bannerResource; 156 this.mUid = uid; 157 this.mSettingsActivityName = settingsActivityName; 158 } 159 160 @UnsupportedAppUsage ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost)161 public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws 162 XmlPullParserException, IOException { 163 ServiceInfo si = info.serviceInfo; 164 XmlResourceParser parser = null; 165 try { 166 if (onHost) { 167 parser = si.loadXmlMetaData(pm, HostApduService.SERVICE_META_DATA); 168 if (parser == null) { 169 throw new XmlPullParserException("No " + HostApduService.SERVICE_META_DATA + 170 " meta-data"); 171 } 172 } else { 173 parser = si.loadXmlMetaData(pm, OffHostApduService.SERVICE_META_DATA); 174 if (parser == null) { 175 throw new XmlPullParserException("No " + OffHostApduService.SERVICE_META_DATA + 176 " meta-data"); 177 } 178 } 179 180 int eventType = parser.getEventType(); 181 while (eventType != XmlPullParser.START_TAG && eventType != XmlPullParser.END_DOCUMENT) { 182 eventType = parser.next(); 183 } 184 185 String tagName = parser.getName(); 186 if (onHost && !"host-apdu-service".equals(tagName)) { 187 throw new XmlPullParserException( 188 "Meta-data does not start with <host-apdu-service> tag"); 189 } else if (!onHost && !"offhost-apdu-service".equals(tagName)) { 190 throw new XmlPullParserException( 191 "Meta-data does not start with <offhost-apdu-service> tag"); 192 } 193 194 Resources res = pm.getResourcesForApplication(si.applicationInfo); 195 AttributeSet attrs = Xml.asAttributeSet(parser); 196 if (onHost) { 197 TypedArray sa = res.obtainAttributes(attrs, 198 com.android.internal.R.styleable.HostApduService); 199 mService = info; 200 mDescription = sa.getString( 201 com.android.internal.R.styleable.HostApduService_description); 202 mRequiresDeviceUnlock = sa.getBoolean( 203 com.android.internal.R.styleable.HostApduService_requireDeviceUnlock, 204 false); 205 mRequiresDeviceScreenOn = sa.getBoolean( 206 com.android.internal.R.styleable.HostApduService_requireDeviceScreenOn, 207 true); 208 mBannerResourceId = sa.getResourceId( 209 com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1); 210 mSettingsActivityName = sa.getString( 211 com.android.internal.R.styleable.HostApduService_settingsActivity); 212 mOffHostName = null; 213 mStaticOffHostName = mOffHostName; 214 sa.recycle(); 215 } else { 216 TypedArray sa = res.obtainAttributes(attrs, 217 com.android.internal.R.styleable.OffHostApduService); 218 mService = info; 219 mDescription = sa.getString( 220 com.android.internal.R.styleable.OffHostApduService_description); 221 mRequiresDeviceUnlock = sa.getBoolean( 222 com.android.internal.R.styleable.OffHostApduService_requireDeviceUnlock, 223 false); 224 mRequiresDeviceScreenOn = sa.getBoolean( 225 com.android.internal.R.styleable.OffHostApduService_requireDeviceScreenOn, 226 false); 227 mBannerResourceId = sa.getResourceId( 228 com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1); 229 mSettingsActivityName = sa.getString( 230 com.android.internal.R.styleable.HostApduService_settingsActivity); 231 mOffHostName = sa.getString( 232 com.android.internal.R.styleable.OffHostApduService_secureElementName); 233 if (mOffHostName != null) { 234 if (mOffHostName.equals("eSE")) { 235 mOffHostName = "eSE1"; 236 } else if (mOffHostName.equals("SIM")) { 237 mOffHostName = "SIM1"; 238 } 239 } 240 mStaticOffHostName = mOffHostName; 241 sa.recycle(); 242 } 243 244 mStaticAidGroups = new HashMap<String, AidGroup>(); 245 mDynamicAidGroups = new HashMap<String, AidGroup>(); 246 mOnHost = onHost; 247 248 final int depth = parser.getDepth(); 249 AidGroup currentGroup = null; 250 251 // Parsed values for the current AID group 252 while (((eventType = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 253 && eventType != XmlPullParser.END_DOCUMENT) { 254 tagName = parser.getName(); 255 if (eventType == XmlPullParser.START_TAG && "aid-group".equals(tagName) && 256 currentGroup == null) { 257 final TypedArray groupAttrs = res.obtainAttributes(attrs, 258 com.android.internal.R.styleable.AidGroup); 259 // Get category of AID group 260 String groupCategory = groupAttrs.getString( 261 com.android.internal.R.styleable.AidGroup_category); 262 String groupDescription = groupAttrs.getString( 263 com.android.internal.R.styleable.AidGroup_description); 264 if (!CardEmulation.CATEGORY_PAYMENT.equals(groupCategory)) { 265 groupCategory = CardEmulation.CATEGORY_OTHER; 266 } 267 currentGroup = mStaticAidGroups.get(groupCategory); 268 if (currentGroup != null) { 269 if (!CardEmulation.CATEGORY_OTHER.equals(groupCategory)) { 270 Log.e(TAG, "Not allowing multiple aid-groups in the " + 271 groupCategory + " category"); 272 currentGroup = null; 273 } 274 } else { 275 currentGroup = new AidGroup(groupCategory, groupDescription); 276 } 277 groupAttrs.recycle(); 278 } else if (eventType == XmlPullParser.END_TAG && "aid-group".equals(tagName) && 279 currentGroup != null) { 280 if (currentGroup.aids.size() > 0) { 281 if (!mStaticAidGroups.containsKey(currentGroup.category)) { 282 mStaticAidGroups.put(currentGroup.category, currentGroup); 283 } 284 } else { 285 Log.e(TAG, "Not adding <aid-group> with empty or invalid AIDs"); 286 } 287 currentGroup = null; 288 } else if (eventType == XmlPullParser.START_TAG && "aid-filter".equals(tagName) && 289 currentGroup != null) { 290 final TypedArray a = res.obtainAttributes(attrs, 291 com.android.internal.R.styleable.AidFilter); 292 String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). 293 toUpperCase(); 294 if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { 295 currentGroup.aids.add(aid); 296 } else { 297 Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); 298 } 299 a.recycle(); 300 } else if (eventType == XmlPullParser.START_TAG && 301 "aid-prefix-filter".equals(tagName) && currentGroup != null) { 302 final TypedArray a = res.obtainAttributes(attrs, 303 com.android.internal.R.styleable.AidFilter); 304 String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). 305 toUpperCase(); 306 // Add wildcard char to indicate prefix 307 aid = aid.concat("*"); 308 if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { 309 currentGroup.aids.add(aid); 310 } else { 311 Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); 312 } 313 a.recycle(); 314 } else if (eventType == XmlPullParser.START_TAG && 315 tagName.equals("aid-suffix-filter") && currentGroup != null) { 316 final TypedArray a = res.obtainAttributes(attrs, 317 com.android.internal.R.styleable.AidFilter); 318 String aid = a.getString(com.android.internal.R.styleable.AidFilter_name). 319 toUpperCase(); 320 // Add wildcard char to indicate suffix 321 aid = aid.concat("#"); 322 if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) { 323 currentGroup.aids.add(aid); 324 } else { 325 Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid); 326 } 327 a.recycle(); 328 } 329 } 330 } catch (NameNotFoundException e) { 331 throw new XmlPullParserException("Unable to create context for: " + si.packageName); 332 } finally { 333 if (parser != null) parser.close(); 334 } 335 // Set uid 336 mUid = si.applicationInfo.uid; 337 } 338 getComponent()339 public ComponentName getComponent() { 340 return new ComponentName(mService.serviceInfo.packageName, 341 mService.serviceInfo.name); 342 } 343 getOffHostSecureElement()344 public String getOffHostSecureElement() { 345 return mOffHostName; 346 } 347 348 /** 349 * Returns a consolidated list of AIDs from the AID groups 350 * registered by this service. Note that if a service has both 351 * a static (manifest-based) AID group for a category and a dynamic 352 * AID group, only the dynamically registered AIDs will be returned 353 * for that category. 354 * @return List of AIDs registered by the service 355 */ getAids()356 public List<String> getAids() { 357 final ArrayList<String> aids = new ArrayList<String>(); 358 for (AidGroup group : getAidGroups()) { 359 aids.addAll(group.aids); 360 } 361 return aids; 362 } 363 getPrefixAids()364 public List<String> getPrefixAids() { 365 final ArrayList<String> prefixAids = new ArrayList<String>(); 366 for (AidGroup group : getAidGroups()) { 367 for (String aid : group.aids) { 368 if (aid.endsWith("*")) { 369 prefixAids.add(aid); 370 } 371 } 372 } 373 return prefixAids; 374 } 375 getSubsetAids()376 public List<String> getSubsetAids() { 377 final ArrayList<String> subsetAids = new ArrayList<String>(); 378 for (AidGroup group : getAidGroups()) { 379 for (String aid : group.aids) { 380 if (aid.endsWith("#")) { 381 subsetAids.add(aid); 382 } 383 } 384 } 385 return subsetAids; 386 } 387 /** 388 * Returns the registered AID group for this category. 389 */ getDynamicAidGroupForCategory(String category)390 public AidGroup getDynamicAidGroupForCategory(String category) { 391 return mDynamicAidGroups.get(category); 392 } 393 removeDynamicAidGroupForCategory(String category)394 public boolean removeDynamicAidGroupForCategory(String category) { 395 return (mDynamicAidGroups.remove(category) != null); 396 } 397 398 /** 399 * Returns a consolidated list of AID groups 400 * registered by this service. Note that if a service has both 401 * a static (manifest-based) AID group for a category and a dynamic 402 * AID group, only the dynamically registered AID group will be returned 403 * for that category. 404 * @return List of AIDs registered by the service 405 */ getAidGroups()406 public ArrayList<AidGroup> getAidGroups() { 407 final ArrayList<AidGroup> groups = new ArrayList<AidGroup>(); 408 for (Map.Entry<String, AidGroup> entry : mDynamicAidGroups.entrySet()) { 409 groups.add(entry.getValue()); 410 } 411 for (Map.Entry<String, AidGroup> entry : mStaticAidGroups.entrySet()) { 412 if (!mDynamicAidGroups.containsKey(entry.getKey())) { 413 // Consolidate AID groups - don't return static ones 414 // if a dynamic group exists for the category. 415 groups.add(entry.getValue()); 416 } 417 } 418 return groups; 419 } 420 421 /** 422 * Returns the category to which this service has attributed the AID that is passed in, 423 * or null if we don't know this AID. 424 */ getCategoryForAid(String aid)425 public String getCategoryForAid(String aid) { 426 ArrayList<AidGroup> groups = getAidGroups(); 427 for (AidGroup group : groups) { 428 if (group.aids.contains(aid.toUpperCase())) { 429 return group.category; 430 } 431 } 432 return null; 433 } 434 hasCategory(String category)435 public boolean hasCategory(String category) { 436 return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category)); 437 } 438 439 @UnsupportedAppUsage isOnHost()440 public boolean isOnHost() { 441 return mOnHost; 442 } 443 444 @UnsupportedAppUsage requiresUnlock()445 public boolean requiresUnlock() { 446 return mRequiresDeviceUnlock; 447 } 448 449 /** 450 * Returns whether this service should only be started when the device is screen on. 451 */ requiresScreenOn()452 public boolean requiresScreenOn() { 453 return mRequiresDeviceScreenOn; 454 } 455 456 @UnsupportedAppUsage getDescription()457 public String getDescription() { 458 return mDescription; 459 } 460 461 @UnsupportedAppUsage getUid()462 public int getUid() { 463 return mUid; 464 } 465 setOrReplaceDynamicAidGroup(AidGroup aidGroup)466 public void setOrReplaceDynamicAidGroup(AidGroup aidGroup) { 467 mDynamicAidGroups.put(aidGroup.getCategory(), aidGroup); 468 } 469 470 /** 471 * Sets the off host Secure Element. 472 * @param offHost Secure Element to set. Only accept strings with prefix SIM or prefix eSE. 473 * Ref: GSMA TS.26 - NFC Handset Requirements 474 * TS26_NFC_REQ_069: For UICC, Secure Element Name SHALL be SIM[smartcard slot] 475 * (e.g. SIM/SIM1, SIM2… SIMn). 476 * TS26_NFC_REQ_070: For embedded SE, Secure Element Name SHALL be eSE[number] 477 * (e.g. eSE/eSE1, eSE2, etc.). 478 */ setOffHostSecureElement(String offHost)479 public void setOffHostSecureElement(String offHost) { 480 mOffHostName = offHost; 481 } 482 483 /** 484 * Resets the off host Secure Element to statically defined 485 * by the service in the manifest file. 486 */ unsetOffHostSecureElement()487 public void unsetOffHostSecureElement() { 488 mOffHostName = mStaticOffHostName; 489 } 490 loadLabel(PackageManager pm)491 public CharSequence loadLabel(PackageManager pm) { 492 return mService.loadLabel(pm); 493 } 494 loadAppLabel(PackageManager pm)495 public CharSequence loadAppLabel(PackageManager pm) { 496 try { 497 return pm.getApplicationLabel(pm.getApplicationInfo( 498 mService.resolvePackageName, PackageManager.GET_META_DATA)); 499 } catch (PackageManager.NameNotFoundException e) { 500 return null; 501 } 502 } 503 loadIcon(PackageManager pm)504 public Drawable loadIcon(PackageManager pm) { 505 return mService.loadIcon(pm); 506 } 507 508 @UnsupportedAppUsage loadBanner(PackageManager pm)509 public Drawable loadBanner(PackageManager pm) { 510 Resources res; 511 try { 512 res = pm.getResourcesForApplication(mService.serviceInfo.packageName); 513 Drawable banner = res.getDrawable(mBannerResourceId); 514 return banner; 515 } catch (NotFoundException e) { 516 Log.e(TAG, "Could not load banner."); 517 return null; 518 } catch (NameNotFoundException e) { 519 Log.e(TAG, "Could not load banner."); 520 return null; 521 } 522 } 523 524 @UnsupportedAppUsage getSettingsActivityName()525 public String getSettingsActivityName() { return mSettingsActivityName; } 526 527 @Override toString()528 public String toString() { 529 StringBuilder out = new StringBuilder("ApduService: "); 530 out.append(getComponent()); 531 out.append(", UID: " + mUid); 532 out.append(", description: " + mDescription); 533 out.append(", Static AID Groups: "); 534 for (AidGroup aidGroup : mStaticAidGroups.values()) { 535 out.append(aidGroup.toString()); 536 } 537 out.append(", Dynamic AID Groups: "); 538 for (AidGroup aidGroup : mDynamicAidGroups.values()) { 539 out.append(aidGroup.toString()); 540 } 541 return out.toString(); 542 } 543 544 @Override equals(@ullable Object o)545 public boolean equals(@Nullable Object o) { 546 if (this == o) return true; 547 if (!(o instanceof ApduServiceInfo)) return false; 548 ApduServiceInfo thatService = (ApduServiceInfo) o; 549 550 return thatService.getComponent().equals(this.getComponent()) 551 && thatService.getUid() == this.getUid(); 552 } 553 554 @Override hashCode()555 public int hashCode() { 556 return getComponent().hashCode(); 557 } 558 559 560 @Override describeContents()561 public int describeContents() { 562 return 0; 563 } 564 565 @Override writeToParcel(Parcel dest, int flags)566 public void writeToParcel(Parcel dest, int flags) { 567 mService.writeToParcel(dest, flags); 568 dest.writeString(mDescription); 569 dest.writeInt(mOnHost ? 1 : 0); 570 dest.writeString(mOffHostName); 571 dest.writeString(mStaticOffHostName); 572 dest.writeInt(mStaticAidGroups.size()); 573 if (mStaticAidGroups.size() > 0) { 574 dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values())); 575 } 576 dest.writeInt(mDynamicAidGroups.size()); 577 if (mDynamicAidGroups.size() > 0) { 578 dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values())); 579 } 580 dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); 581 dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0); 582 dest.writeInt(mBannerResourceId); 583 dest.writeInt(mUid); 584 dest.writeString(mSettingsActivityName); 585 }; 586 587 @UnsupportedAppUsage 588 public static final @android.annotation.NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = 589 new Parcelable.Creator<ApduServiceInfo>() { 590 @Override 591 public ApduServiceInfo createFromParcel(Parcel source) { 592 ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); 593 String description = source.readString(); 594 boolean onHost = source.readInt() != 0; 595 String offHostName = source.readString(); 596 String staticOffHostName = source.readString(); 597 ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>(); 598 int numStaticGroups = source.readInt(); 599 if (numStaticGroups > 0) { 600 source.readTypedList(staticAidGroups, AidGroup.CREATOR); 601 } 602 ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>(); 603 int numDynamicGroups = source.readInt(); 604 if (numDynamicGroups > 0) { 605 source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); 606 } 607 boolean requiresUnlock = source.readInt() != 0; 608 boolean requiresScreenOn = source.readInt() != 0; 609 int bannerResource = source.readInt(); 610 int uid = source.readInt(); 611 String settingsActivityName = source.readString(); 612 return new ApduServiceInfo(info, onHost, description, staticAidGroups, 613 dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, 614 settingsActivityName, offHostName, staticOffHostName); 615 } 616 617 @Override 618 public ApduServiceInfo[] newArray(int size) { 619 return new ApduServiceInfo[size]; 620 } 621 }; 622 dump(FileDescriptor fd, PrintWriter pw, String[] args)623 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 624 pw.println(" " + getComponent() 625 + " (Description: " + getDescription() + ")" 626 + " (UID: " + getUid() + ")"); 627 if (mOnHost) { 628 pw.println(" On Host Service"); 629 } else { 630 pw.println(" Off-host Service"); 631 pw.println(" " + "Current off-host SE:" + mOffHostName 632 + " static off-host SE:" + mStaticOffHostName); 633 } 634 pw.println(" Static AID groups:"); 635 for (AidGroup group : mStaticAidGroups.values()) { 636 pw.println(" Category: " + group.category); 637 for (String aid : group.aids) { 638 pw.println(" AID: " + aid); 639 } 640 } 641 pw.println(" Dynamic AID groups:"); 642 for (AidGroup group : mDynamicAidGroups.values()) { 643 pw.println(" Category: " + group.category); 644 for (String aid : group.aids) { 645 pw.println(" AID: " + aid); 646 } 647 } 648 pw.println(" Settings Activity: " + mSettingsActivityName); 649 pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); 650 pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); 651 } 652 653 /** 654 * Dump debugging info as ApduServiceInfoProto 655 * 656 * If the output belongs to a sub message, the caller is responsible for wrapping this function 657 * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. 658 * See proto definition in frameworks/base/core/proto/android/nfc/apdu_service_info.proto 659 * 660 * @param proto the ProtoOutputStream to write to 661 */ dumpDebug(ProtoOutputStream proto)662 public void dumpDebug(ProtoOutputStream proto) { 663 Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME); 664 proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); 665 proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); 666 if (!mOnHost) { 667 proto.write(ApduServiceInfoProto.OFF_HOST_NAME, mOffHostName); 668 proto.write(ApduServiceInfoProto.STATIC_OFF_HOST_NAME, mStaticOffHostName); 669 } 670 for (AidGroup group : mStaticAidGroups.values()) { 671 long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); 672 group.dump(proto); 673 proto.end(token); 674 } 675 for (AidGroup group : mDynamicAidGroups.values()) { 676 long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); 677 group.dump(proto); 678 proto.end(token); 679 } 680 proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName); 681 } 682 } 683