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(", description: " + mDescription); 532 out.append(", Static AID Groups: "); 533 for (AidGroup aidGroup : mStaticAidGroups.values()) { 534 out.append(aidGroup.toString()); 535 } 536 out.append(", Dynamic AID Groups: "); 537 for (AidGroup aidGroup : mDynamicAidGroups.values()) { 538 out.append(aidGroup.toString()); 539 } 540 return out.toString(); 541 } 542 543 @Override equals(@ullable Object o)544 public boolean equals(@Nullable Object o) { 545 if (this == o) return true; 546 if (!(o instanceof ApduServiceInfo)) return false; 547 ApduServiceInfo thatService = (ApduServiceInfo) o; 548 549 return thatService.getComponent().equals(this.getComponent()); 550 } 551 552 @Override hashCode()553 public int hashCode() { 554 return getComponent().hashCode(); 555 } 556 557 558 @Override describeContents()559 public int describeContents() { 560 return 0; 561 } 562 563 @Override writeToParcel(Parcel dest, int flags)564 public void writeToParcel(Parcel dest, int flags) { 565 mService.writeToParcel(dest, flags); 566 dest.writeString(mDescription); 567 dest.writeInt(mOnHost ? 1 : 0); 568 dest.writeString(mOffHostName); 569 dest.writeString(mStaticOffHostName); 570 dest.writeInt(mStaticAidGroups.size()); 571 if (mStaticAidGroups.size() > 0) { 572 dest.writeTypedList(new ArrayList<AidGroup>(mStaticAidGroups.values())); 573 } 574 dest.writeInt(mDynamicAidGroups.size()); 575 if (mDynamicAidGroups.size() > 0) { 576 dest.writeTypedList(new ArrayList<AidGroup>(mDynamicAidGroups.values())); 577 } 578 dest.writeInt(mRequiresDeviceUnlock ? 1 : 0); 579 dest.writeInt(mRequiresDeviceScreenOn ? 1 : 0); 580 dest.writeInt(mBannerResourceId); 581 dest.writeInt(mUid); 582 dest.writeString(mSettingsActivityName); 583 }; 584 585 @UnsupportedAppUsage 586 public static final @android.annotation.NonNull Parcelable.Creator<ApduServiceInfo> CREATOR = 587 new Parcelable.Creator<ApduServiceInfo>() { 588 @Override 589 public ApduServiceInfo createFromParcel(Parcel source) { 590 ResolveInfo info = ResolveInfo.CREATOR.createFromParcel(source); 591 String description = source.readString(); 592 boolean onHost = source.readInt() != 0; 593 String offHostName = source.readString(); 594 String staticOffHostName = source.readString(); 595 ArrayList<AidGroup> staticAidGroups = new ArrayList<AidGroup>(); 596 int numStaticGroups = source.readInt(); 597 if (numStaticGroups > 0) { 598 source.readTypedList(staticAidGroups, AidGroup.CREATOR); 599 } 600 ArrayList<AidGroup> dynamicAidGroups = new ArrayList<AidGroup>(); 601 int numDynamicGroups = source.readInt(); 602 if (numDynamicGroups > 0) { 603 source.readTypedList(dynamicAidGroups, AidGroup.CREATOR); 604 } 605 boolean requiresUnlock = source.readInt() != 0; 606 boolean requiresScreenOn = source.readInt() != 0; 607 int bannerResource = source.readInt(); 608 int uid = source.readInt(); 609 String settingsActivityName = source.readString(); 610 return new ApduServiceInfo(info, onHost, description, staticAidGroups, 611 dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, 612 settingsActivityName, offHostName, staticOffHostName); 613 } 614 615 @Override 616 public ApduServiceInfo[] newArray(int size) { 617 return new ApduServiceInfo[size]; 618 } 619 }; 620 dump(FileDescriptor fd, PrintWriter pw, String[] args)621 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 622 pw.println(" " + getComponent() + 623 " (Description: " + getDescription() + ")"); 624 if (mOnHost) { 625 pw.println(" On Host Service"); 626 } else { 627 pw.println(" Off-host Service"); 628 pw.println(" " + "Current off-host SE:" + mOffHostName 629 + " static off-host SE:" + mStaticOffHostName); 630 } 631 pw.println(" Static AID groups:"); 632 for (AidGroup group : mStaticAidGroups.values()) { 633 pw.println(" Category: " + group.category); 634 for (String aid : group.aids) { 635 pw.println(" AID: " + aid); 636 } 637 } 638 pw.println(" Dynamic AID groups:"); 639 for (AidGroup group : mDynamicAidGroups.values()) { 640 pw.println(" Category: " + group.category); 641 for (String aid : group.aids) { 642 pw.println(" AID: " + aid); 643 } 644 } 645 pw.println(" Settings Activity: " + mSettingsActivityName); 646 pw.println(" Requires Device Unlock: " + mRequiresDeviceUnlock); 647 pw.println(" Requires Device ScreenOn: " + mRequiresDeviceScreenOn); 648 } 649 650 /** 651 * Dump debugging info as ApduServiceInfoProto 652 * 653 * If the output belongs to a sub message, the caller is responsible for wrapping this function 654 * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. 655 * See proto definition in frameworks/base/core/proto/android/nfc/apdu_service_info.proto 656 * 657 * @param proto the ProtoOutputStream to write to 658 */ dumpDebug(ProtoOutputStream proto)659 public void dumpDebug(ProtoOutputStream proto) { 660 getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME); 661 proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); 662 proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); 663 if (!mOnHost) { 664 proto.write(ApduServiceInfoProto.OFF_HOST_NAME, mOffHostName); 665 proto.write(ApduServiceInfoProto.STATIC_OFF_HOST_NAME, mStaticOffHostName); 666 } 667 for (AidGroup group : mStaticAidGroups.values()) { 668 long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); 669 group.dump(proto); 670 proto.end(token); 671 } 672 for (AidGroup group : mDynamicAidGroups.values()) { 673 long token = proto.start(ApduServiceInfoProto.STATIC_AID_GROUPS); 674 group.dump(proto); 675 proto.end(token); 676 } 677 proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName); 678 } 679 } 680