Get entitlement configuration from intent extra

Tethering resource configuration is move from framwork to tethering
module. The resource would not be accessible from outside of tethering
module.
List the replacements of framework resources usage and intent extra:
1. R.string.config_mobile_hotspot_provision_response
    --> android.net.extra.TETHER_PROVISIONING_RESPONSE.
2. R.string.config_mobile_hotspot_provision_app_no_ui
    --> android.net.extra.TETHER_UI_PROVISIONING_APP_NAME
3. R.array.config_mobile_hotspot_provision_app
    --> android.net.extra.TETHER_SILENT_PROVISIONING_ACTION
Besides, the current active subId would put in
android.net.extra.TETHER_SUBID

Note: They are not APIs because of API freeze. Now both tethering module
and Settings define these strings independently. Will replace hard code
string as tethering module-lib APIs in b/159085857.

Also move the entitlement response intent registeration from onCreated
to onStartCommand, this can avoid wrong intent registeration if subId
changed between onCreate and when the intent arrived.

Bug: 146918263
Test: atest TetherServiceTest
      atest TetherProvisioningActivityTest

Change-Id: I3d06df01302a9c1f0893712d9250fe394dc66588
This commit is contained in:
markchien
2020-06-07 17:56:02 +08:00
committed by Mark Chien
parent e5c9b113b6
commit e22bd7a2e8
4 changed files with 119 additions and 73 deletions

View File

@@ -18,7 +18,6 @@ package com.android.settings.network;
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 static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
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 android.util.Log;
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 @@ public class TetherProvisioningActivity extends Activity {
@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 @@ public class TetherProvisioningActivity extends Activity {
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 @@ public class TetherProvisioningActivity extends Activity {
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]);

View File

@@ -41,7 +41,6 @@ import android.content.IntentFilter;
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 android.util.Log;
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 @@ public class TetherService extends Service {
@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 @@ public class TetherService extends Service {
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 class TetherService extends Service {
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 @@ public class TetherService extends Service {
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 @@ public class TetherService extends Service {
}
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 @@ public class TetherService extends Service {
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 @@ public class TetherService extends Service {
}
}
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 @@ public class TetherService extends Service {
} 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 @@ public class TetherService extends Service {
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 @@ public class TetherService extends Service {
}
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 @@ public class TetherService extends Service {
@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 @@ public class TetherService extends Service {
/**
* 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 @@ public class TetherService extends Service {
return SubscriptionManager.getActiveDataSubscriptionId();
}
}
@VisibleForTesting
Resources getResourceForActiveDataSubId() {
final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId();
return Utils.getResourcesForSubId(this, subId);
}
}

View File

@@ -18,9 +18,10 @@ package com.android.settings.network;
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 @@ public class TetherProvisioningActivityTest {
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 class TetherProvisioningActivityTest {
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 @@ public class TetherProvisioningActivityTest {
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 */);

View File

@@ -27,6 +27,10 @@ import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
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 @@ public class TetherServiceTest extends ServiceTestCase<TetherService> {
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 @@ public class TetherServiceTest extends ServiceTestCase<TetherService> {
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);
}