Add NFC APIs for dynamic resources.

For new Tap & Pay settings UX.

Change-Id: I55ff4e4d4a4b6d26c3c88d96431c4f14d0963323
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index f10e530..3d065e3 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -17,6 +17,7 @@
 package android.nfc.cardemulation;
 
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
@@ -28,6 +29,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.ResultReceiver;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
@@ -88,12 +90,24 @@
      * The uid of the package the service belongs to
      */
     final int mUid;
+
+    /**
+     * Whether this service has dynamic resources
+     */
+    final boolean mHasDynamicResources;
+
+    /**
+     * Settings Activity for this service
+     */
+    final String mSettingsActivityName;
+
     /**
      * @hide
      */
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
-            boolean requiresUnlock, int bannerResource, int uid) {
+            boolean requiresUnlock, int bannerResource, int uid, boolean hasDynamicResources,
+            String settingsActivityName) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
@@ -108,6 +122,8 @@
         }
         this.mBannerResourceId = bannerResource;
         this.mUid = uid;
+        this.mHasDynamicResources = hasDynamicResources;
+        this.mSettingsActivityName = settingsActivityName;
     }
 
     public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
@@ -156,6 +172,10 @@
                         false);
                 mBannerResourceId = sa.getResourceId(
                         com.android.internal.R.styleable.HostApduService_apduServiceBanner, -1);
+                mHasDynamicResources = sa.getBoolean(
+                        com.android.internal.R.styleable.HostApduService_dynamicResources, false);
+                mSettingsActivityName = sa.getString(
+                        com.android.internal.R.styleable.HostApduService_settingsActivity);
                 sa.recycle();
             } else {
                 TypedArray sa = res.obtainAttributes(attrs,
@@ -166,6 +186,10 @@
                 mRequiresDeviceUnlock = false;
                 mBannerResourceId = sa.getResourceId(
                         com.android.internal.R.styleable.OffHostApduService_apduServiceBanner, -1);
+                mHasDynamicResources = sa.getBoolean(
+                        com.android.internal.R.styleable.OffHostApduService_dynamicResources, false);
+                mSettingsActivityName = sa.getString(
+                        com.android.internal.R.styleable.HostApduService_settingsActivity);
                 sa.recycle();
             }
 
@@ -359,6 +383,15 @@
         return mService.loadLabel(pm);
     }
 
+    public CharSequence loadAppLabel(PackageManager pm) {
+        try {
+            return pm.getApplicationLabel(pm.getApplicationInfo(
+                    mService.resolvePackageName, PackageManager.GET_META_DATA));
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
     public Drawable loadIcon(PackageManager pm) {
         return mService.loadIcon(pm);
     }
@@ -377,6 +410,11 @@
             return null;
         }
     }
+    public boolean hasDynamicResources() {
+        return mHasDynamicResources;
+    }
+
+    public String getSettingsActivityName() { return mSettingsActivityName; }
 
     @Override
     public String toString() {
@@ -430,6 +468,8 @@
         dest.writeInt(mRequiresDeviceUnlock ? 1 : 0);
         dest.writeInt(mBannerResourceId);
         dest.writeInt(mUid);
+        dest.writeInt(mHasDynamicResources ? 1 : 0);
+        dest.writeString(mSettingsActivityName);
     };
 
     public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
@@ -452,8 +492,11 @@
             boolean requiresUnlock = source.readInt() != 0;
             int bannerResource = source.readInt();
             int uid = source.readInt();
+            boolean dynamicResources = source.readInt() != 0;
+            String settingsActivityName = source.readString();
             return new ApduServiceInfo(info, onHost, description, staticAidGroups,
-                    dynamicAidGroups, requiresUnlock, bannerResource, uid);
+                    dynamicAidGroups, requiresUnlock, bannerResource, uid, dynamicResources,
+                    settingsActivityName);
         }
 
         @Override
@@ -479,5 +522,6 @@
                 pw.println("            AID: " + aid);
             }
         }
+        pw.println("    Settings Activity: " + mSettingsActivityName);
     }
 }
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 64c2bc2..b94d4a6 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -90,6 +90,37 @@
     public static final String CATEGORY_OTHER = "other";
 
     /**
+     * Ordered broadcast that can be sent to your app to
+     * request a description and banner to be shown in
+     * Android Settings UI.
+     * When sent to you, this broadcast will contain the
+     * {@link #EXTRA_SERVICE_COMPONENT} extra to identify
+     * the service.
+     *
+     * Note that this broadcast will only be sent to your
+     * app, if a card emulation service in your app has requested
+     * its resources to be loaded dynamically.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_REQUEST_SERVICE_RESOURCES =
+            "android.nfc.cardemulation.action.REQUEST_SERVICE_RESOURCES";
+
+    /**
+     * The description of the service. Note that this must
+     * be localized by your app, as the String will be shown
+     * as is.
+     */
+    public static final String EXTRA_DESCRIPTION =
+            "android.nfc.cardemulation.extra.DESCRIPTION";
+
+    /**
+     * The resource ID of the service banner to be shown
+     * for this service.
+     */
+    public static final String EXTRA_BANNER_RES_ID =
+            "android.nfc.cardemulation.extra.BANNER_RES_ID";
+
+    /**
      * Return value for {@link #getSelectionModeForCategory(String)}.
      *
      * <p>In this mode, the user has set a default service for this
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1f529ca..fa5e4ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -227,6 +227,8 @@
     <protected-broadcast android:name="com.android.nfc_extras.action.AID_SELECTED" />
 
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
+
+    <protected-broadcast android:name="android.nfc.cardemulation.action.REQUEST_SERVICE_RESOURCES" />
     <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
     <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ef438ab..551c083 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3191,6 +3191,11 @@
         <!-- A drawable that can be rendered in Android's system UI for representing
              the service. -->
         <attr name="apduServiceBanner" format="reference"/>
+        <!-- Allows the app to update the description and service banner at run-time -->
+        <attr name="dynamicResources" format="boolean"/>
+        <!-- Component name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity"/>
     </declare-styleable>
 
     <!-- Use <code>offhost-apdu-service</code> as the root tag of the XML resource that
@@ -3204,6 +3209,11 @@
         <!-- A drawable that can be rendered in Android's system UI for representing
              the service. -->
         <attr name="apduServiceBanner"/>
+        <!-- Allows the app to update the description and service banner at run-time -->
+        <attr name="dynamicResources"/>
+        <!-- Component name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity"/>
     </declare-styleable>
 
     <!-- Specify one or more <code>aid-group</code> elements inside a
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c157d4c..7252584 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2672,4 +2672,6 @@
   <public type="attr" name="rightIndents" />
 
   <public type="attr" name="showForAllUsers" />
+  <!-- NFC CardEmulation: dynamically load service resources -->
+  <public type="attr" name="dynamicResources" />
 </resources>