Support configurable AIDs for ARAM

1. Try to retrieve ARs from a configurable list of AIDs.
   If all AIDs are not accessible, then try default AID.
2. Add PKG_REF_DO/PERM_AR_DO for AR parsing.

Bug:139078767
Test: Access rules could be retrieved from different AIDs.

Change-Id: Id14418ebd39646d4b50e2ad240f50bc74507f816
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..cc310a0
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- The list of AIDs are the candidate of the ARA AID in ESE.
+         The first available AID will be taken as the ARA AID. -->
+    <string-array name="config_ara_aid_candidate_list_ese" translatable="false" />
+</resources>
diff --git a/src/com/android/se/Terminal.java b/src/com/android/se/Terminal.java
index e17ac88..0062206 100644
--- a/src/com/android/se/Terminal.java
+++ b/src/com/android/se/Terminal.java
@@ -712,6 +712,10 @@
         return mAccessControlEnforcer;
     }
 
+    public Context getContext() {
+        return mContext;
+    }
+
     /** Dump data for debug purpose . */
     public void dump(PrintWriter writer) {
         writer.println("SECURE ELEMENT SERVICE TERMINAL: " + mName);
diff --git a/src/com/android/se/security/AccessControlEnforcer.java b/src/com/android/se/security/AccessControlEnforcer.java
index 22b82f0..90843a8 100644
--- a/src/com/android/se/security/AccessControlEnforcer.java
+++ b/src/com/android/se/security/AccessControlEnforcer.java
@@ -84,7 +84,10 @@
         mAccessRuleCache = new AccessRuleCache();
     }
 
-    public static byte[] getDefaultAccessControlAid() {
+    public byte[] getDefaultAccessControlAid() {
+        if (mAraController != null) {
+            return mAraController.getAccessControlAid();
+        }
         return AraController.getAraMAid();
     }
 
@@ -454,6 +457,14 @@
 
         writer.println("mUseArf: " + mUseArf);
         writer.println("mUseAra: " + mUseAra);
+        if (mUseAra && mAraController != null) {
+            if (getDefaultAccessControlAid() == null) {
+                writer.println("AraInUse: default applet");
+            } else {
+                writer.println("AraInUse: " + ByteArrayConverter.byteArrayToHexString(
+                        getDefaultAccessControlAid()));
+            }
+        }
         writer.println("mInitialChannelAccess:");
         writer.println(mInitialChannelAccess.toString());
         writer.println();
diff --git a/src/com/android/se/security/ara/AraController.java b/src/com/android/se/security/ara/AraController.java
index bc4ea19..52223b6 100644
--- a/src/com/android/se/security/ara/AraController.java
+++ b/src/com/android/se/security/ara/AraController.java
@@ -35,10 +35,15 @@
 
 package com.android.se.security.ara;
 
+import android.content.Context;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.se.Channel;
+import com.android.se.R;
+import com.android.se.SecureElementService;
 import com.android.se.Terminal;
+import com.android.se.internal.ByteArrayConverter;
 import com.android.se.security.AccessRuleCache;
 import com.android.se.security.ChannelAccess;
 import com.android.se.security.gpac.BerTlv;
@@ -73,27 +78,65 @@
     private AccessRuleCache mAccessRuleCache = null;
     private Terminal mTerminal = null;
     private AccessRuleApplet mApplet = null;
+    private Context mContext = null;
+    private String[] mAids = new String[0];
+    private byte[] mAccessControlAid = ARA_M_AID;
 
     public AraController(AccessRuleCache cache, Terminal terminal) {
         mAccessRuleCache = cache;
         mTerminal = terminal;
+        mContext = mTerminal.getContext();
+        if (mTerminal.getName().startsWith(SecureElementService.ESE_TERMINAL)) {
+            mAids = mContext.getResources().getStringArray(
+                    R.array.config_ara_aid_candidate_list_ese);
+        }
     }
 
     public static byte[] getAraMAid() {
         return ARA_M_AID;
     }
 
+    public byte[] getAccessControlAid() {
+        return mAccessControlAid;
+    }
+
     /**
      * Initialize the AraController, reads the refresh tag.
      * and fetch the access rules
      */
     public synchronized void initialize() throws IOException, NoSuchElementException {
-        Channel channel = mTerminal.openLogicalChannelWithoutChannelAccess(getAraMAid());
-        if (channel == null) {
-            throw new MissingResourceException("could not open channel", "", "");
+        Channel channel = null;
+        byte[] aid = null;
+
+        // try to fetch access rules from ARA Aid list
+        for (String araAid : mAids) {
+            if (!TextUtils.isEmpty(araAid)) {
+                aid = ByteArrayConverter.hexStringToByteArray(araAid);
+            } else {
+                aid = null;
+            }
+            try {
+                channel = mTerminal.openLogicalChannelWithoutChannelAccess(aid);
+                if (channel == null) {
+                    throw new MissingResourceException("could not open channel", "", "");
+                }
+                mAccessControlAid = aid;
+                break;
+            } catch (NoSuchElementException e) {
+                Log.i(mTag, "applet:" + araAid + " is not accessible");
+                continue;
+            }
         }
 
-        // set access conditions to access ARA-M.
+        // try to fetch access rules from ARA-M if all ARA in Aid list is not accessible
+        if (channel == null) {
+            channel = mTerminal.openLogicalChannelWithoutChannelAccess(getAraMAid());
+            if (channel == null) {
+                throw new MissingResourceException("could not open channel", "", "");
+            }
+        }
+
+        // set access conditions to access ARA.
         ChannelAccess araChannelAccess = new ChannelAccess();
         araChannelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, "");
         araChannelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED);
@@ -104,7 +147,7 @@
             mApplet = new AccessRuleApplet(channel);
             byte[] tag = mApplet.readRefreshTag();
             // if refresh tag is equal to the previous one it is not
-            // neccessary to read all rules again.
+            // necessary to read all rules again.
             if (mAccessRuleCache.isRefreshTagEqual(tag)) {
                 Log.i(mTag, "Refresh tag unchanged. Using access rules from cache.");
                 return;
diff --git a/src/com/android/se/security/gpac/AR_DO.java b/src/com/android/se/security/gpac/AR_DO.java
index ebb5a6e..491b099 100755
--- a/src/com/android/se/security/gpac/AR_DO.java
+++ b/src/com/android/se/security/gpac/AR_DO.java
@@ -49,15 +49,31 @@
 
     private APDU_AR_DO mApduAr = null;
     private NFC_AR_DO mNfcAr = null;
+    private PERM_AR_DO mPermAr = null;
 
     public AR_DO(byte[] rawData, int valueIndex, int valueLength) {
         super(rawData, TAG, valueIndex, valueLength);
     }
 
+    public AR_DO(APDU_AR_DO apduArDo, NFC_AR_DO nfcArDo, PERM_AR_DO permArDo) {
+        super(null, TAG, 0, 0);
+        mApduAr = apduArDo;
+        mNfcAr = nfcArDo;
+        mPermAr = permArDo;
+    }
+
     public AR_DO(APDU_AR_DO apduArDo, NFC_AR_DO nfcArDo) {
         super(null, TAG, 0, 0);
         mApduAr = apduArDo;
         mNfcAr = nfcArDo;
+        mPermAr = null;
+    }
+
+    public AR_DO(PERM_AR_DO permArDo) {
+        super(null, TAG, 0, 0);
+        mApduAr = null;
+        mNfcAr = null;
+        mPermAr = permArDo;
     }
 
     public APDU_AR_DO getApduArDo() {
@@ -68,19 +84,27 @@
         return mNfcAr;
     }
 
+    public PERM_AR_DO getPermArDo() {
+        return mPermAr;
+    }
+
     @Override
     /**
      * Interpret value.
      *
      * <p>Tag: E3
      *
-     * <p>Value: Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A concatenation
-     * of one or two AR-DO(s). If two AR-DO(s) are present these must have different types.
+     * <p>Value:
+     *     1. Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A
+     *        concatenation of one or two AR-DO(s). If two AR-DO(s) are present these must have
+     *        different types.
+     *     2. PERM-AR-DO
      */
     public void interpret() throws ParserException {
 
         this.mApduAr = null;
         this.mNfcAr = null;
+        this.mPermAr = null;
 
         byte[] data = getRawData();
         int index = getValueIndex();
@@ -98,6 +122,9 @@
             } else if (temp.getTag() == NFC_AR_DO.TAG) { // NFC-AR-DO
                 mNfcAr = new NFC_AR_DO(data, temp.getValueIndex(), temp.getValueLength());
                 mNfcAr.interpret();
+            } else if (temp.getTag() == PERM_AR_DO.TAG) { // PERM-AR-DO
+                mPermAr = new PERM_AR_DO(data, temp.getValueIndex(), temp.getValueLength());
+                mPermAr.interpret();
             } else {
                 // un-comment following line if a more restrictive
                 // behavior is necessary.
@@ -106,7 +133,7 @@
             index = temp.getValueIndex() + temp.getValueLength();
         } while (getValueIndex() + getValueLength() > index);
 
-        if (mApduAr == null && mNfcAr == null) {
+        if (mApduAr == null && mNfcAr == null && mPermAr == null) {
             throw new ParserException("No valid DO in AR-DO!");
         }
     }
@@ -117,8 +144,11 @@
      *
      * <p>Tag: E3
      *
-     * <p>Value: Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A concatenation
-     * of one or two AR-DO(s). If two AR-DO(s) are present these must have different types.
+     * <p>Value:
+     *     1. Value can contain APDU-AR-DO or NFC-AR-DO or APDU-AR-DO | NFC-AR-DO A
+     *        concatenation of one or two AR-DO(s). If two AR-DO(s) are present these must have
+     *        different types.
+     *     2. PERM-AR-DO
      */
     public void build(ByteArrayOutputStream stream) throws DO_Exception {
 
@@ -134,6 +164,10 @@
             mNfcAr.build(temp);
         }
 
+        if (mPermAr != null) {
+            mPermAr.build(temp);
+        }
+
         BerTlv.encodeLength(temp.size(), stream);
         try {
             stream.write(temp.toByteArray());
diff --git a/src/com/android/se/security/gpac/PERM_AR_DO.java b/src/com/android/se/security/gpac/PERM_AR_DO.java
new file mode 100644
index 0000000..f927042
--- /dev/null
+++ b/src/com/android/se/security/gpac/PERM_AR_DO.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.se.security.gpac;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * PERM-AR-DO: The access rule for Device API control consists of a permission bit mask of 8 bytes.
+ */
+public class PERM_AR_DO extends BerTlv {
+
+    public static final int TAG = 0xDB;
+
+    public static final int PERM_MASK_LEN = 8;
+
+    private byte[] mPermissionMask = new byte[0];
+
+    public PERM_AR_DO(byte[] rawData, int valueIndex, int valueLength) {
+        super(rawData, TAG, valueIndex, valueLength);
+    }
+
+    public PERM_AR_DO(byte[] permissionMask) {
+        super(permissionMask, TAG, 0, (permissionMask == null ? 0 : permissionMask.length));
+        if (permissionMask != null) mPermissionMask = permissionMask;
+    }
+
+    public byte[] getPermissionMask() {
+        return mPermissionMask;
+    }
+
+    @Override
+    /**
+     * Tag: DB Length: 8 Value: Contains a permission mask
+     */
+    public void interpret() throws ParserException {
+        mPermissionMask = new byte[0];
+
+        byte[] data = getRawData();
+        int index = getValueIndex();
+        int length = getValueLength();
+
+        if (index + getValueLength() > data.length) {
+            throw new ParserException("Not enough data for PERM-AR-DO!");
+        }
+
+        if (length == PERM_MASK_LEN) {
+            mPermissionMask = new byte[length];
+            System.arraycopy(data, index, mPermissionMask, 0, length);
+        } else {
+            throw new ParserException("Invalid length of PERM-AR-DO!");
+        }
+    }
+
+    @Override
+    /**
+     * Tag: DB Length: 8 Value: Contains a permission mask
+     */
+    public void build(ByteArrayOutputStream stream) throws DO_Exception {
+
+        // sanity checks
+        if (mPermissionMask.length != PERM_MASK_LEN) {
+            throw new DO_Exception("Invalid value length for PERM-AR-DO!");
+        }
+
+        // write tag
+        stream.write(getTag());
+        try {
+            stream.write(mPermissionMask.length);
+            stream.write(mPermissionMask);
+        } catch (IOException ioe) {
+            throw new DO_Exception("PERM could not be written!");
+        }
+    }
+}
diff --git a/src/com/android/se/security/gpac/PKG_REF_DO.java b/src/com/android/se/security/gpac/PKG_REF_DO.java
new file mode 100644
index 0000000..a35415a
--- /dev/null
+++ b/src/com/android/se/security/gpac/PKG_REF_DO.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.se.security.gpac;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * PKG-REF-DO: The PKG-REF-DO is used for retrieving and storing the corresponding access rules
+ * for a device application (which is identified by the package name) from and to
+ * the ARA
+ */
+public class PKG_REF_DO extends BerTlv {
+
+    public static final int TAG = 0xCA;
+
+    private String mPackageName = null;
+
+    public PKG_REF_DO(byte[] rawData, int valueIndex, int valueLength) {
+        super(rawData, TAG, valueIndex, valueLength);
+    }
+
+    public PKG_REF_DO(byte[] pkg) {
+        super(pkg, TAG, 0, (pkg == null ? 0 : pkg.length));
+        if (pkg != null) {
+            mPackageName = new String(pkg);
+        }
+    }
+
+    public PKG_REF_DO() {
+        super(null, TAG, 0, 0);
+    }
+
+    /**
+     * Comapares two PKG_REF_DO objects and returns true if they are equal
+     */
+    public static boolean equals(PKG_REF_DO obj1, PKG_REF_DO obj2) {
+        if (obj1 == null) {
+            return (obj2 == null) ? true : false;
+        }
+        return obj1.equals(obj2);
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Override
+    public String toString() {
+        return new String("PKG_REF_DO: " + mPackageName);
+    }
+
+    /**
+     * Tags: CA Length: max length is 128 bytes
+     *
+     * <p>Value: pkg: identifies ASCII encoded package name
+     *
+     */
+    @Override
+    public void interpret() throws ParserException {
+        byte[] data = getRawData();
+        int index = getValueIndex();
+
+        // sanity checks
+        if (getValueLength() > 128) {
+            throw new ParserException("Invalid value length for PKG-REF-DO!");
+        }
+
+        if (index + getValueLength() > data.length) {
+            throw new ParserException("Not enough data for PKG-REF-DO!");
+        }
+
+        byte[] pkg = new byte[getValueLength()];
+        System.arraycopy(data, index, pkg, 0, getValueLength());
+        mPackageName = new String(pkg);
+    }
+
+    /**
+     * Tags: CA Length: max length is 128 bytes
+     *
+     * <p>Value: pkg: identifies ASCII encoded package name
+     *
+     */
+    @Override
+    public void build(ByteArrayOutputStream stream) throws DO_Exception {
+        byte[] pkg = mPackageName.getBytes();
+        // sanity checks
+        if (pkg.length > 128) {
+            throw new DO_Exception("Invalid value length for PKG-REF-DO!");
+        }
+
+        stream.write(getTag());
+
+        try {
+            stream.write(pkg.length);
+            stream.write(pkg);
+        } catch (IOException ioe) {
+            throw new DO_Exception("PKG could not be written!");
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof PKG_REF_DO) {
+            PKG_REF_DO pkg_ref_do = (PKG_REF_DO) obj;
+            if (getTag() == pkg_ref_do.getTag()) {
+                if (mPackageName != null) {
+                    return mPackageName.equals(pkg_ref_do.mPackageName);
+                } else {
+                    return (pkg_ref_do.mPackageName == null);
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        try {
+            this.build(stream);
+        } catch (DO_Exception e) {
+            return 1;
+        }
+        byte[] data = stream.toByteArray();
+        int hash = Arrays.hashCode(data);
+        return hash;
+    }
+}
diff --git a/src/com/android/se/security/gpac/REF_DO.java b/src/com/android/se/security/gpac/REF_DO.java
index 7ccf547..0400e96 100644
--- a/src/com/android/se/security/gpac/REF_DO.java
+++ b/src/com/android/se/security/gpac/REF_DO.java
@@ -47,15 +47,24 @@
 
     private AID_REF_DO mAidDo = null;
     private Hash_REF_DO mHashDo = null;
+    private PKG_REF_DO mPkgDo = null;
 
     public REF_DO(byte[] rawData, int valueIndex, int valueLength) {
         super(rawData, TAG, valueIndex, valueLength);
     }
 
+    public REF_DO(AID_REF_DO aidRefDo, Hash_REF_DO hashRefDo, PKG_REF_DO pkgRefDo) {
+        super(null, TAG, 0, 0);
+        mAidDo = aidRefDo;
+        mHashDo = hashRefDo;
+        mPkgDo = pkgRefDo;
+    }
+
     public REF_DO(AID_REF_DO aidRefDo, Hash_REF_DO hashRefDo) {
         super(null, TAG, 0, 0);
         mAidDo = aidRefDo;
         mHashDo = hashRefDo;
+        mPkgDo = null;
     }
 
     @Override
@@ -68,6 +77,10 @@
         }
         if (mHashDo != null) {
             b.append(mHashDo.toString());
+            b.append(' ');
+        }
+        if (mPkgDo != null) {
+            b.append(mPkgDo.toString());
         }
         return b.toString();
     }
@@ -80,12 +93,19 @@
         return mHashDo;
     }
 
+    public PKG_REF_DO getPkgDo() {
+        return mPkgDo;
+    }
+
     /**
      * Interpret data.
      *
      * <p>Tags: E1 -> Length: n
      *
-     * <p>Value: AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+     * <p>Value:
+     *    1. AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+     *    2. Hash-REF-DO or Hash-REF-DO | PKG-REF-DO A concatenation of a Hash-REF-DO and a
+     *       PKG-REF-DO.
      *
      * <p>Length: n bytes.
      */
@@ -94,6 +114,7 @@
 
         mAidDo = null;
         mHashDo = null;
+        mPkgDo = null;
 
         byte[] data = getRawData();
         int index = getValueIndex();
@@ -113,6 +134,9 @@
             } else if (temp.getTag() == Hash_REF_DO.TAG) { // Hash-REF-DO
                 mHashDo = new Hash_REF_DO(data, temp.getValueIndex(), temp.getValueLength());
                 mHashDo.interpret();
+            } else if (temp.getTag() == PKG_REF_DO.TAG) { // PKG-REF-DO
+                mPkgDo = new PKG_REF_DO(data, temp.getValueIndex(), temp.getValueLength());
+                mPkgDo.interpret();
             } else {
                 // uncomment following line if a more restrictive
                 // behaviour is necessary.
@@ -121,6 +145,10 @@
             index = temp.getValueIndex() + temp.getValueLength();
         } while (getValueIndex() + getValueLength() > index);
 
+        if (mAidDo != null && !mAidDo.isCarrierPrivilege() && mHashDo != null && mPkgDo != null) {
+            throw new ParserException("Unexpected combination of SEAC DOs and DAC DO");
+        }
+
         // A rule without AID is a Carrier Privilege Rule.
         // Enforces the AID to be the Carrier Privilege AID to avoid a null AID.
         if (mAidDo == null && mHashDo != null) {
@@ -138,19 +166,30 @@
     }
 
     /**
-     * Tag: E1 Length: n Value: AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a
-     * Hash-REF-DO.
+     * Tag: E1 Length: n Value:
+     *     1. AID-REF-DO | Hash-REF-DO: A concatenation of an AID-REF-DO and a Hash-REF-DO.
+     *     2. Hash-REF-DO or Hash-REF-DO | PKG-REF-DO A concatenation of a Hash-REF-DO and a
+     *        PKG-REF-DO.
      */
     @Override
     public void build(ByteArrayOutputStream stream) throws DO_Exception {
         ByteArrayOutputStream temp = new ByteArrayOutputStream();
 
-        if (mAidDo == null || mHashDo == null) {
+        if (mHashDo == null) {
             throw new DO_Exception("REF-DO: Required DO missing!");
         }
 
-        mAidDo.build(temp);
-        mHashDo.build(temp);
+        if (mAidDo != null) {
+            mAidDo.build(temp);
+        }
+
+        if (mHashDo != null) {
+            mHashDo.build(temp);
+        }
+
+        if (mPkgDo != null) {
+            mPkgDo.build(temp);
+        }
 
         byte[] data = temp.toByteArray();
         BerTlv tlv = new BerTlv(data, getTag(), 0, data.length);
@@ -164,7 +203,9 @@
             if (getTag() == ref_do.getTag()) {
                 if (AID_REF_DO.equals(mAidDo, ref_do.mAidDo)) {
                     if (Hash_REF_DO.equals(mHashDo, ref_do.mHashDo)) {
-                        return true;
+                        if (PKG_REF_DO.equals(mPkgDo, ref_do.mPkgDo)) {
+                            return true;
+                        }
                     }
                 }
             }