Merge "Get entitlement configuration from intent extra" into rvc-dev
diff --git a/src/com/android/settings/network/TetherProvisioningActivity.java b/src/com/android/settings/network/TetherProvisioningActivity.java
index bb61546..047c90d 100644
--- a/src/com/android/settings/network/TetherProvisioningActivity.java
+++ b/src/com/android/settings/network/TetherProvisioningActivity.java
@@ -18,7 +18,6 @@
 
 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
 import static android.net.TetheringManager.TETHERING_INVALID;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
@@ -28,7 +27,6 @@
 import android.app.Activity;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
@@ -37,8 +35,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.settings.Utils;
-
 /**
  * Activity which acts as a proxy to the tether provisioning app for sanity checks and permission
  * restrictions. Specifically, the provisioning apps require
@@ -53,7 +49,10 @@
     @VisibleForTesting
     static final int PROVISION_REQUEST = 0;
     @VisibleForTesting
-    static final String EXTRA_SUBID = "subId";
+    static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
+    @VisibleForTesting
+    public static final String EXTRA_TETHER_UI_PROVISIONING_APP_NAME =
+            "android.net.extra.TETHER_UI_PROVISIONING_APP_NAME";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -62,7 +61,8 @@
 
         final int tetherType = getIntent().getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID);
 
-        final int tetherSubId = getIntent().getIntExtra(EXTRA_SUBID, INVALID_SUBSCRIPTION_ID);
+        final int tetherSubId = getIntent().getIntExtra(
+                EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID);
         final int subId = SubscriptionManager.getActiveDataSubscriptionId();
         if (tetherSubId != subId) {
             Log.e(TAG, "This Provisioning request is outdated, current subId: " + subId);
@@ -70,11 +70,11 @@
             finish();
             return;
         }
-        String[] provisionApp = getIntent().getStringArrayExtra(EXTRA_RUN_PROVISION);
-        if (provisionApp == null || provisionApp.length < 2) {
-            final Resources res = Utils.getResourcesForSubId(this, subId);
-            provisionApp = res.getStringArray(
-                    com.android.internal.R.array.config_mobile_hotspot_provision_app);
+        String[] provisionApp = getIntent().getStringArrayExtra(
+                EXTRA_TETHER_UI_PROVISIONING_APP_NAME);
+        if (provisionApp == null || provisionApp.length != 2) {
+            Log.e(TAG, "Unexpected provision app configuration");
+            return;
         }
         final Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.setClassName(provisionApp[0], provisionApp[1]);
diff --git a/src/com/android/settings/wifi/tether/TetherService.java b/src/com/android/settings/wifi/tether/TetherService.java
index 72ea1a9..5902719 100644
--- a/src/com/android/settings/wifi/tether/TetherService.java
+++ b/src/com/android/settings/wifi/tether/TetherService.java
@@ -41,7 +41,6 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
 import android.net.TetheringManager;
 import android.os.IBinder;
 import android.os.ResultReceiver;
@@ -52,10 +51,9 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.settings.Utils;
-
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 public class TetherService extends Service {
     private static final String TAG = "TetherService";
@@ -64,7 +62,13 @@
     @VisibleForTesting
     public static final String EXTRA_RESULT = "EntitlementResult";
     @VisibleForTesting
-    public static final String EXTRA_SUBID = "subId";
+    public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID";
+    @VisibleForTesting
+    public static final String EXTRA_TETHER_PROVISIONING_RESPONSE =
+            "android.net.extra.TETHER_PROVISIONING_RESPONSE";
+    @VisibleForTesting
+    public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION =
+            "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION";
 
     // Activity results to match the activity provision protocol.
     // Default to something not ok.
@@ -79,6 +83,11 @@
 
     private int mCurrentTypeIndex;
     private boolean mInProvisionCheck;
+    /** Intent action received from the provisioning app when entitlement check completes. */
+    private String mExpectedProvisionResponseAction = null;
+    /** Intent action sent to the provisioning app to request an entitlement check. */
+    private String mProvisionAction;
+    private int mSubId = INVALID_SUBSCRIPTION_ID;
     private TetherServiceWrapper mWrapper;
     private ArrayList<Integer> mCurrentTethers;
     private ArrayMap<Integer, List<ResultReceiver>> mPendingCallbacks;
@@ -92,10 +101,6 @@
     public void onCreate() {
         super.onCreate();
         if (DEBUG) Log.d(TAG, "Creating TetherService");
-        String provisionResponse = getResourceForActiveDataSubId().getString(
-                com.android.internal.R.string.config_mobile_hotspot_provision_response);
-        registerReceiver(mReceiver, new IntentFilter(provisionResponse),
-                android.Manifest.permission.TETHER_PRIVILEGED, null);
         SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
         mCurrentTethers = stringToTethers(prefs.getString(KEY_TETHERS, ""));
         mCurrentTypeIndex = 0;
@@ -106,10 +111,28 @@
         mPendingCallbacks.put(TETHERING_ETHERNET, new ArrayList<ResultReceiver>());
     }
 
+    // Registers the broadcast receiver for the specified response action, first unregistering
+    // the receiver if it was registered for a different response action.
+    private void maybeRegisterReceiver(final String responseAction) {
+        if (Objects.equals(responseAction, mExpectedProvisionResponseAction)) return;
+
+        if (mExpectedProvisionResponseAction != null) unregisterReceiver(mReceiver);
+
+        registerReceiver(mReceiver, new IntentFilter(responseAction),
+                android.Manifest.permission.TETHER_PRIVILEGED, null /* handler */);
+        mExpectedProvisionResponseAction = responseAction;
+        if (DEBUG) Log.d(TAG, "registerReceiver " + responseAction);
+    }
+
+    private int stopSelfAndStartNotSticky() {
+        stopSelf();
+        return START_NOT_STICKY;
+    }
+
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        if (intent.hasExtra(EXTRA_SUBID)) {
-            final int tetherSubId = intent.getIntExtra(EXTRA_SUBID, INVALID_SUBSCRIPTION_ID);
+        if (intent.hasExtra(EXTRA_TETHER_SUBID)) {
+            final int tetherSubId = intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID);
             final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId();
             if (tetherSubId != subId) {
                 Log.e(TAG, "This Provisioning request is outdated, current subId: " + subId);
@@ -118,7 +141,9 @@
                 }
                 return START_NOT_STICKY;
             }
+            mSubId = subId;
         }
+
         if (intent.hasExtra(EXTRA_ADD_TETHER_TYPE)) {
             int type = intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID);
             ResultReceiver callback = intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK);
@@ -128,9 +153,9 @@
                     callbacksForType.add(callback);
                 } else {
                     // Invalid tether type. Just ignore this request and report failure.
+                    Log.e(TAG, "Invalid tethering type " + type + ", stopping");
                     callback.send(TETHER_ERROR_UNKNOWN_IFACE, null);
-                    stopSelf();
-                    return START_NOT_STICKY;
+                    return stopSelfAndStartNotSticky();
                 }
             }
 
@@ -140,6 +165,19 @@
             }
         }
 
+        mProvisionAction = intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION);
+        if (mProvisionAction == null) {
+            Log.e(TAG, "null provisioning action, stop ");
+            return stopSelfAndStartNotSticky();
+        }
+
+        final String response = intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE);
+        if (response == null) {
+            Log.e(TAG, "null provisioning response, stop ");
+            return stopSelfAndStartNotSticky();
+        }
+        maybeRegisterReceiver(response);
+
         if (intent.hasExtra(EXTRA_REM_TETHER_TYPE)) {
             if (!mInProvisionCheck) {
                 int type = intent.getIntExtra(EXTRA_REM_TETHER_TYPE, TETHERING_INVALID);
@@ -158,8 +196,7 @@
         } else if (!mInProvisionCheck) {
             // If we aren't running any provisioning, no reason to stay alive.
             if (DEBUG) Log.d(TAG, "Stopping self.  startid: " + startId);
-            stopSelf();
-            return START_NOT_STICKY;
+            return stopSelfAndStartNotSticky();
         }
         // We want to be started if we are killed accidently, so that we can be sure we finish
         // the check.
@@ -175,7 +212,10 @@
         SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
         prefs.edit().putString(KEY_TETHERS, tethersToString(mCurrentTethers)).commit();
 
-        unregisterReceiver(mReceiver);
+        if (mExpectedProvisionResponseAction != null) {
+            unregisterReceiver(mReceiver);
+            mExpectedProvisionResponseAction = null;
+        }
         if (DEBUG) Log.d(TAG, "Destroying TetherService");
         super.onDestroy();
     }
@@ -220,26 +260,26 @@
     }
 
     private void startProvisioning(int index) {
-        if (index < mCurrentTethers.size()) {
-            Intent intent = getProvisionBroadcastIntent(index);
-            setEntitlementAppActive(index);
+        if (index >= mCurrentTethers.size()) return;
 
-            if (DEBUG) Log.d(TAG, "Sending provisioning broadcast: " + intent.getAction()
+        Intent intent = getProvisionBroadcastIntent(index);
+        setEntitlementAppActive(index);
+
+        if (DEBUG) {
+            Log.d(TAG, "Sending provisioning broadcast: " + intent.getAction()
                     + " type: " + mCurrentTethers.get(index));
-
-            sendBroadcast(intent);
-            mInProvisionCheck = true;
         }
+
+        sendBroadcast(intent);
+        mInProvisionCheck = true;
     }
 
     private Intent getProvisionBroadcastIntent(int index) {
-        String provisionAction = getResourceForActiveDataSubId().getString(
-                com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui);
-        final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId();
-        Intent intent = new Intent(provisionAction);
+        if (mProvisionAction == null) Log.wtf(TAG, "null provisioning action");
+        Intent intent = new Intent(mProvisionAction);
         int type = mCurrentTethers.get(index);
         intent.putExtra(TETHER_CHOICE, type);
-        intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId);
+        intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, mSubId);
         intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND
                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
 
@@ -282,27 +322,30 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (DEBUG) Log.d(TAG, "Got provision result " + intent);
-            String provisionResponse = getResourceForActiveDataSubId().getString(
-                    com.android.internal.R.string.config_mobile_hotspot_provision_response);
 
-            if (provisionResponse.equals(intent.getAction())) {
-                if (!mInProvisionCheck) {
-                    Log.e(TAG, "Unexpected provision response " + intent);
-                    return;
-                }
-                int checkType = mCurrentTethers.get(mCurrentTypeIndex);
-                mInProvisionCheck = false;
-                int result = intent.getIntExtra(EXTRA_RESULT, RESULT_DEFAULT);
-                if (result != RESULT_OK) disableTethering(checkType);
-                fireCallbacksForType(checkType, result);
+            if (!intent.getAction().equals(mExpectedProvisionResponseAction)) {
+                Log.e(TAG, "Received provisioning response for unexpected action="
+                        + intent.getAction() + ", expected=" + mExpectedProvisionResponseAction);
+                return;
+            }
 
-                if (++mCurrentTypeIndex >= mCurrentTethers.size()) {
-                    // We are done with all checks, time to die.
-                    stopSelf();
-                } else {
-                    // Start the next check in our list.
-                    startProvisioning(mCurrentTypeIndex);
-                }
+            if (!mInProvisionCheck) {
+                Log.e(TAG, "Unexpected provisioning response when not in provisioning check"
+                        + intent);
+                return;
+            }
+            int checkType = mCurrentTethers.get(mCurrentTypeIndex);
+            mInProvisionCheck = false;
+            int result = intent.getIntExtra(EXTRA_RESULT, RESULT_DEFAULT);
+            if (result != RESULT_OK) disableTethering(checkType);
+            fireCallbacksForType(checkType, result);
+
+            if (++mCurrentTypeIndex >= mCurrentTethers.size()) {
+                // We are done with all checks, time to die.
+                stopSelf();
+            } else {
+                // Start the next check in our list.
+                startProvisioning(mCurrentTypeIndex);
             }
         }
     };
@@ -321,8 +364,7 @@
 
     /**
      * A static helper class used for tests. UsageStatsManager cannot be mocked out because
-     * it's marked final. Static method SubscriptionManager#getResourcesForSubId also cannot
-     * be mocked. This class can be mocked out instead.
+     * it's marked final. This class can be mocked out instead.
      */
     @VisibleForTesting
     public static class TetherServiceWrapper {
@@ -341,10 +383,4 @@
             return SubscriptionManager.getActiveDataSubscriptionId();
         }
     }
-
-    @VisibleForTesting
-    Resources getResourceForActiveDataSubId() {
-        final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId();
-        return Utils.getResourcesForSubId(this, subId);
-    }
 }
diff --git a/tests/unit/src/com/android/settings/network/TetherProvisioningActivityTest.java b/tests/unit/src/com/android/settings/network/TetherProvisioningActivityTest.java
index 6246d9f..af29dcc 100644
--- a/tests/unit/src/com/android/settings/network/TetherProvisioningActivityTest.java
+++ b/tests/unit/src/com/android/settings/network/TetherProvisioningActivityTest.java
@@ -18,9 +18,10 @@
 
 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
-import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
 import static android.net.TetheringManager.TETHERING_WIFI;
 
+import static com.android.settings.network.TetherProvisioningActivity.EXTRA_TETHER_SUBID;
+import static com.android.settings.network.TetherProvisioningActivity.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
 import static com.android.settings.network.TetherProvisioningActivity.PROVISION_REQUEST;
 
 import static org.junit.Assert.assertEquals;
@@ -72,7 +73,7 @@
                 new Intent(Settings.ACTION_TETHER_PROVISIONING_UI)
                         .putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI)
                         .putExtra(EXTRA_PROVISION_CALLBACK, receiver)
-                        .putExtra(TetherProvisioningActivity.EXTRA_SUBID, 10000))) {
+                        .putExtra(TetherProvisioningActivity.EXTRA_TETHER_SUBID, 10000))) {
             assertEquals(TetheringManager.TETHER_ERROR_PROVISIONING_FAILED, receiver.get());
             assertEquals(Lifecycle.State.DESTROYED, scenario.getState());
         }
@@ -82,12 +83,13 @@
     public void testOnCreate_FinishWithUnavailableProvisioningApp() throws Exception {
         final WrappedReceiver receiver = new WrappedReceiver();
         final int subId = SubscriptionManager.getActiveDataSubscriptionId();
+        final String[] emptyProvisioningApp = { "", "" };
         try (ActivityScenario<TetherProvisioningActivity> scenario = ActivityScenario.launch(
                 new Intent(Settings.ACTION_TETHER_PROVISIONING_UI)
                         .putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI)
                         .putExtra(EXTRA_PROVISION_CALLBACK, receiver)
-                        .putExtra(TetherProvisioningActivity.EXTRA_SUBID, subId)
-                        .putExtra(EXTRA_RUN_PROVISION, new String[] { "", "" }))) {
+                        .putExtra(EXTRA_TETHER_SUBID, subId)
+                        .putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, emptyProvisioningApp))) {
             assertEquals(TetheringManager.TETHER_ERROR_PROVISIONING_FAILED, receiver.get());
             assertEquals(Lifecycle.State.DESTROYED, scenario.getState());
         }
@@ -105,8 +107,8 @@
                 new Intent(Settings.ACTION_TETHER_PROVISIONING_UI)
                         .putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI)
                         .putExtra(EXTRA_PROVISION_CALLBACK, receiver)
-                        .putExtra(TetherProvisioningActivity.EXTRA_SUBID, subId)
-                        .putExtra(EXTRA_RUN_PROVISION, provisionApp))) {
+                        .putExtra(EXTRA_TETHER_SUBID, subId)
+                        .putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, provisionApp))) {
             scenario.onActivity(activity -> {
                 assertFalse(activity.isFinishing());
                 activity.onActivityResult(PROVISION_REQUEST, Activity.RESULT_OK, null /* intent */);
diff --git a/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java b/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
index 19f29c0..be030bf 100644
--- a/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
+++ b/tests/unit/src/com/android/settings/wifi/tether/TetherServiceTest.java
@@ -27,6 +27,10 @@
 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
+import static com.android.settings.wifi.tether.TetherService.EXTRA_TETHER_PROVISIONING_RESPONSE;
+import static com.android.settings.wifi.tether.TetherService.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
+import static com.android.settings.wifi.tether.TetherService.EXTRA_TETHER_SUBID;
+
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
@@ -228,8 +232,10 @@
         Intent intent = new Intent();
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
         intent.putExtra(EXTRA_RUN_PROVISION, true);
+        intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, TEST_NO_UI_ACTION);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
-        intent.putExtra(TetherService.EXTRA_SUBID, 1 /* Tested subId number */);
+        intent.putExtra(EXTRA_TETHER_SUBID, 1 /* Tested subId number */);
+        intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, TEST_RESPONSE_ACTION);
         startService(intent);
 
         SystemClock.sleep(PROVISION_TIMEOUT);
@@ -242,8 +248,10 @@
         Intent intent = new Intent();
         intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
         intent.putExtra(EXTRA_RUN_PROVISION, true);
+        intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, TEST_NO_UI_ACTION);
         intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
-        intent.putExtra(TetherService.EXTRA_SUBID, INVALID_SUBSCRIPTION_ID);
+        intent.putExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID);
+        intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, TEST_RESPONSE_ACTION);
         startService(intent);
     }