Merge "Refactor the EntitlementManager"
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 049a9f6..d978541 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -45,13 +45,13 @@
 import android.os.SystemProperties;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
-import android.util.ArraySet;
 import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.StateMachine;
 
 import java.io.PrintWriter;
+import java.util.BitSet;
 
 /**
  * Re-check tethering provisioning for enabled downstream tether types.
@@ -74,11 +74,11 @@
     private final ComponentName mSilentProvisioningService;
     private static final int MS_PER_HOUR = 60 * 60 * 1000;
 
-    // The ArraySet contains enabled downstream types, ex:
+    // The BitSet is the bit map of each enabled downstream types, ex:
     // {@link TetheringManager.TETHERING_WIFI}
     // {@link TetheringManager.TETHERING_USB}
     // {@link TetheringManager.TETHERING_BLUETOOTH}
-    private final ArraySet<Integer> mCurrentTethers;
+    private final BitSet mCurrentDownstreams;
     private final Context mContext;
     private final int mPermissionChangeMessageCode;
     private final SharedLog mLog;
@@ -87,9 +87,9 @@
     private final StateMachine mTetherMasterSM;
     // Key: TetheringManager.TETHERING_*(downstream).
     // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
-    private final SparseIntArray mCellularPermitted;
+    private final SparseIntArray mCurrentEntitlementResults;
     private PendingIntent mProvisioningRecheckAlarm;
-    private boolean mCellularUpstreamPermitted = true;
+    private boolean mLastCellularUpstreamPermitted = true;
     private boolean mUsingCellularAsUpstream = false;
     private boolean mNeedReRunProvisioningUi = false;
     private OnUiEntitlementFailedListener mListener;
@@ -97,11 +97,10 @@
 
     public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
             int permissionChangeMessageCode) {
-
         mContext = ctx;
         mLog = log.forSubComponent(TAG);
-        mCurrentTethers = new ArraySet<Integer>();
-        mCellularPermitted = new SparseIntArray();
+        mCurrentDownstreams = new BitSet();
+        mCurrentEntitlementResults = new SparseIntArray();
         mEntitlementCacheValue = new SparseIntArray();
         mTetherMasterSM = tetherMasterSM;
         mPermissionChangeMessageCode = permissionChangeMessageCode;
@@ -144,13 +143,19 @@
      * Check if cellular upstream is permitted.
      */
     public boolean isCellularUpstreamPermitted() {
-        // If provisioning is required and EntitlementManager don't know any downstream,
-        // cellular upstream should not be allowed.
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-        if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) {
-            return false;
-        }
-        return mCellularUpstreamPermitted;
+
+        return isCellularUpstreamPermitted(config);
+    }
+
+    private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
+        if (!isTetherProvisioningRequired(config)) return true;
+
+        // If provisioning is required and EntitlementManager doesn't know any downstreams,
+        // cellular upstream should not be allowed.
+        if (mCurrentDownstreams.isEmpty()) return false;
+
+        return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
     }
 
     /**
@@ -164,29 +169,22 @@
     public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
         if (!isValidDownstreamType(downstreamType)) return;
 
-        if (!mCurrentTethers.contains(downstreamType)) mCurrentTethers.add(downstreamType);
+        mCurrentDownstreams.set(downstreamType, true);
 
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
-        if (isTetherProvisioningRequired(config)) {
-            // If provisioning is required and the result is not available yet,
-            // cellular upstream should not be allowed.
-            if (mCellularPermitted.size() == 0) {
-                mCellularUpstreamPermitted = false;
-            }
-            // If upstream is not cellular, provisioning app would not be launched
-            // till upstream change to cellular.
-            if (mUsingCellularAsUpstream) {
-                if (showProvisioningUi) {
-                    runUiTetherProvisioning(downstreamType, config.activeDataSubId);
-                } else {
-                    runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
-                }
-                mNeedReRunProvisioningUi = false;
+        if (!isTetherProvisioningRequired(config)) return;
+
+        // If upstream is not cellular, provisioning app would not be launched
+        // till upstream change to cellular.
+        if (mUsingCellularAsUpstream) {
+            if (showProvisioningUi) {
+                runUiTetherProvisioning(downstreamType, config.activeDataSubId);
             } else {
-                mNeedReRunProvisioningUi |= showProvisioningUi;
+                runSilentTetherProvisioning(downstreamType, config.activeDataSubId);
             }
+            mNeedReRunProvisioningUi = false;
         } else {
-            mCellularUpstreamPermitted = true;
+            mNeedReRunProvisioningUi |= showProvisioningUi;
         }
     }
 
@@ -195,14 +193,14 @@
      *
      * @param type tethering type from TetheringManager.TETHERING_{@code *}
      */
-    public void stopProvisioningIfNeeded(int type) {
-        if (!isValidDownstreamType(type)) return;
+    public void stopProvisioningIfNeeded(int downstreamType) {
+        if (!isValidDownstreamType(downstreamType)) return;
 
-        mCurrentTethers.remove(type);
+        mCurrentDownstreams.set(downstreamType, false);
         // There are lurking bugs where the notion of "provisioning required" or
         // "tethering supported" may change without without tethering being notified properly.
         // Remove the mapping all the time no matter provisioning is required or not.
-        removeDownstreamMapping(type);
+        removeDownstreamMapping(downstreamType);
     }
 
     /**
@@ -213,7 +211,7 @@
     public void notifyUpstream(boolean isCellular) {
         if (DBG) {
             mLog.i("notifyUpstream: " + isCellular
-                    + ", mCellularUpstreamPermitted: " + mCellularUpstreamPermitted
+                    + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
                     + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
         }
         mUsingCellularAsUpstream = isCellular;
@@ -231,7 +229,7 @@
     }
 
     private void maybeRunProvisioning(final TetheringConfiguration config) {
-        if (mCurrentTethers.size() == 0 || !isTetherProvisioningRequired(config)) {
+        if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
             return;
         }
 
@@ -239,8 +237,9 @@
         // are allowed. Therefore even if the silent check here ends in a failure and the UI later
         // yields success, then the downstream that got a failure will re-evaluate as a result of
         // the change and get the new correct value.
-        for (Integer downstream : mCurrentTethers) {
-            if (mCellularPermitted.indexOfKey(downstream) < 0) {
+        for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
+                downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
+            if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
                 if (mNeedReRunProvisioningUi) {
                     mNeedReRunProvisioningUi = false;
                     runUiTetherProvisioning(downstream, config.activeDataSubId);
@@ -286,7 +285,7 @@
             mLog.log("reevaluateSimCardProvisioning() don't run in TetherMaster thread");
         }
         mEntitlementCacheValue.clear();
-        mCellularPermitted.clear();
+        mCurrentEntitlementResults.clear();
 
         // TODO: refine provisioning check to isTetherProvisioningRequired() ??
         if (!config.hasMobileHotspotProvisionApp()
@@ -410,22 +409,21 @@
     }
 
     private void evaluateCellularPermission(final TetheringConfiguration config) {
-        final boolean oldPermitted = mCellularUpstreamPermitted;
-        mCellularUpstreamPermitted = (!isTetherProvisioningRequired(config)
-                || mCellularPermitted.indexOfValue(TETHER_ERROR_NO_ERROR) > -1);
+        final boolean oldPermitted = mLastCellularUpstreamPermitted;
+        mLastCellularUpstreamPermitted = isCellularUpstreamPermitted(config);
 
         if (DBG) {
             mLog.i("Cellular permission change from " + oldPermitted
-                    + " to " + mCellularUpstreamPermitted);
+                    + " to " + mLastCellularUpstreamPermitted);
         }
 
-        if (mCellularUpstreamPermitted != oldPermitted) {
-            mLog.log("Cellular permission change: " + mCellularUpstreamPermitted);
+        if (mLastCellularUpstreamPermitted != oldPermitted) {
+            mLog.log("Cellular permission change: " + mLastCellularUpstreamPermitted);
             mTetherMasterSM.sendMessage(mPermissionChangeMessageCode);
         }
         // Only schedule periodic re-check when tether is provisioned
         // and the result is ok.
-        if (mCellularUpstreamPermitted && mCellularPermitted.size() > 0) {
+        if (mLastCellularUpstreamPermitted && mCurrentEntitlementResults.size() > 0) {
             scheduleProvisioningRechecks(config);
         } else {
             cancelTetherProvisioningRechecks();
@@ -441,10 +439,10 @@
      */
     protected void addDownstreamMapping(int type, int resultCode) {
         mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
-                + " ,TetherTypeRequested: " + mCurrentTethers.contains(type));
-        if (!mCurrentTethers.contains(type)) return;
+                + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
+        if (!mCurrentDownstreams.get(type)) return;
 
-        mCellularPermitted.put(type, resultCode);
+        mCurrentEntitlementResults.put(type, resultCode);
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         evaluateCellularPermission(config);
     }
@@ -455,7 +453,7 @@
      */
     protected void removeDownstreamMapping(int type) {
         mLog.i("removeDownstreamMapping: " + type);
-        mCellularPermitted.delete(type);
+        mCurrentEntitlementResults.delete(type);
         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
         evaluateCellularPermission(config);
     }
@@ -488,14 +486,15 @@
      * @param pw {@link PrintWriter} is used to print formatted
      */
     public void dump(PrintWriter pw) {
-        pw.print("mCellularUpstreamPermitted: ");
-        pw.println(mCellularUpstreamPermitted);
-        for (Integer type : mCurrentTethers) {
+        pw.print("isCellularUpstreamPermitted: ");
+        pw.println(isCellularUpstreamPermitted());
+        for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
+                type = mCurrentDownstreams.nextSetBit(type + 1)) {
             pw.print("Type: ");
             pw.print(typeString(type));
-            if (mCellularPermitted.indexOfKey(type) > -1) {
+            if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
                 pw.print(", Value: ");
-                pw.println(errorString(mCellularPermitted.get(type)));
+                pw.println(errorString(mCurrentEntitlementResults.get(type)));
             } else {
                 pw.println(", Value: empty");
             }