Merge "SlicePurchaseActivity handle user data based on contents type" into udc-qpr-dev
This commit is contained in:
@@ -31,9 +31,11 @@ import android.webkit.CookieManager;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.phone.slice.SlicePurchaseController;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Activity that launches when the user clicks on the performance boost notification.
|
||||
@@ -56,11 +58,17 @@ import java.net.URL;
|
||||
public class SlicePurchaseActivity extends Activity {
|
||||
private static final String TAG = "SlicePurchaseActivity";
|
||||
|
||||
private static final int CONTENTS_TYPE_UNSPECIFIED = 0;
|
||||
private static final int CONTENTS_TYPE_JSON = 1;
|
||||
private static final int CONTENTS_TYPE_XML = 2;
|
||||
|
||||
@NonNull private WebView mWebView;
|
||||
@NonNull private Context mApplicationContext;
|
||||
@NonNull private Intent mIntent;
|
||||
@NonNull private URL mUrl;
|
||||
@TelephonyManager.PremiumCapability protected int mCapability;
|
||||
@Nullable private String mUserData;
|
||||
private int mContentsType;
|
||||
private boolean mIsUserTriggeredFinish;
|
||||
|
||||
@Override
|
||||
@@ -72,6 +80,7 @@ public class SlicePurchaseActivity extends Activity {
|
||||
mCapability = mIntent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
|
||||
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
|
||||
String url = mIntent.getStringExtra(SlicePurchaseController.EXTRA_PURCHASE_URL);
|
||||
mUserData = mIntent.getStringExtra(SlicePurchaseController.EXTRA_USER_DATA);
|
||||
mApplicationContext = getApplicationContext();
|
||||
mIsUserTriggeredFinish = true;
|
||||
logd("onCreate: subId=" + subId + ", capability="
|
||||
@@ -81,7 +90,17 @@ public class SlicePurchaseActivity extends Activity {
|
||||
SlicePurchaseBroadcastReceiver.cancelNotification(mApplicationContext, mCapability);
|
||||
|
||||
// Verify purchase URL is valid
|
||||
mUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url);
|
||||
String contentsType = mIntent.getStringExtra(SlicePurchaseController.EXTRA_CONTENTS_TYPE);
|
||||
mContentsType = CONTENTS_TYPE_UNSPECIFIED;
|
||||
if (!TextUtils.isEmpty(contentsType)) {
|
||||
if (contentsType.equals("json")) {
|
||||
mContentsType = CONTENTS_TYPE_JSON;
|
||||
} else if (contentsType.equals("xml")) {
|
||||
mContentsType = CONTENTS_TYPE_XML;
|
||||
}
|
||||
}
|
||||
mUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url, mUserData,
|
||||
mContentsType == CONTENTS_TYPE_UNSPECIFIED);
|
||||
if (mUrl == null) {
|
||||
String error = "Unable to create a purchase URL.";
|
||||
loge(error);
|
||||
@@ -95,6 +114,20 @@ public class SlicePurchaseActivity extends Activity {
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify user data exists if contents type is specified
|
||||
if (mContentsType != CONTENTS_TYPE_UNSPECIFIED && TextUtils.isEmpty(mUserData)) {
|
||||
String error = "Contents type was specified but user data does not exist.";
|
||||
loge(error);
|
||||
Intent data = new Intent();
|
||||
data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
|
||||
SlicePurchaseController.FAILURE_CODE_NO_USER_DATA);
|
||||
data.putExtra(SlicePurchaseController.EXTRA_FAILURE_REASON, error);
|
||||
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
|
||||
mIntent, SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR, data);
|
||||
finishAndRemoveTask();
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify intent is valid
|
||||
if (!SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)) {
|
||||
loge("Not starting SlicePurchaseActivity with an invalid Intent: " + mIntent);
|
||||
@@ -115,9 +148,7 @@ public class SlicePurchaseActivity extends Activity {
|
||||
}
|
||||
|
||||
// Clear any cookies that might be persisted from previous sessions before loading WebView
|
||||
CookieManager.getInstance().removeAllCookies(value -> {
|
||||
setupWebView();
|
||||
});
|
||||
CookieManager.getInstance().removeAllCookies(value -> setupWebView());
|
||||
}
|
||||
|
||||
protected void onPurchaseSuccessful() {
|
||||
@@ -190,14 +221,46 @@ public class SlicePurchaseActivity extends Activity {
|
||||
// Display WebView
|
||||
setContentView(mWebView);
|
||||
|
||||
// Load the URL
|
||||
String userData = mIntent.getStringExtra(SlicePurchaseController.EXTRA_USER_DATA);
|
||||
if (TextUtils.isEmpty(userData)) {
|
||||
logd("Starting WebView with url: " + mUrl.toString());
|
||||
mWebView.loadUrl(mUrl.toString());
|
||||
// Start the WebView
|
||||
startWebView(mWebView, mUrl.toString(), mContentsType, mUserData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the URL to the WebView as either a GET or POST request, based on the contents type:
|
||||
* <ul>
|
||||
* <li>
|
||||
* CONTENTS_TYPE_UNSPECIFIED:
|
||||
* If the user data exists, append it to the purchase URL and load it as a GET request.
|
||||
* If the user data does not exist, load just the purchase URL as a GET request.
|
||||
* </li>
|
||||
* <li>
|
||||
* CONTENTS_TYPE_JSON or CONTENTS_TYPE_XML:
|
||||
* The user data must exist. Send the JSON or XML formatted user data in a POST request.
|
||||
* If the user data is encoded, it must be prefaced by {@code encodedValue=} and will be
|
||||
* encoded in Base64. Decode the user data and send it in the POST request.
|
||||
* </li>
|
||||
* </ul>
|
||||
* @param webView The WebView to start.
|
||||
* @param url The URL to start the WebView with.
|
||||
* @param contentsType The contents type of the userData.
|
||||
* @param userData The user data to send with the GET or POST request, if it exists.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static void startWebView(@NonNull WebView webView, @NonNull String url, int contentsType,
|
||||
@Nullable String userData) {
|
||||
if (contentsType == CONTENTS_TYPE_UNSPECIFIED) {
|
||||
logd("Starting WebView GET with url: " + url);
|
||||
webView.loadUrl(url);
|
||||
} else {
|
||||
logd("Starting WebView with url: " + mUrl.toString() + ", userData=" + userData);
|
||||
mWebView.postUrl(mUrl.toString(), userData.getBytes());
|
||||
byte[] data = userData.getBytes();
|
||||
String[] split = userData.split("encodedValue=");
|
||||
if (split.length > 1) {
|
||||
logd("Decoding encoded value: " + split[1]);
|
||||
data = Base64.getDecoder().decode(split[1]);
|
||||
}
|
||||
logd("Starting WebView POST with url: " + url + ", contentsType: " + contentsType
|
||||
+ ", data: " + new String(data));
|
||||
webView.postUrl(url, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,9 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
|
||||
}
|
||||
|
||||
String purchaseUrl = intent.getStringExtra(SlicePurchaseController.EXTRA_PURCHASE_URL);
|
||||
if (getPurchaseUrl(purchaseUrl) == null) {
|
||||
String userData = intent.getStringExtra(SlicePurchaseController.EXTRA_USER_DATA);
|
||||
String contentsType = intent.getStringExtra(SlicePurchaseController.EXTRA_CONTENTS_TYPE);
|
||||
if (getPurchaseUrl(purchaseUrl, userData, TextUtils.isEmpty(contentsType)) == null) {
|
||||
loge("isIntentValid: invalid purchase URL: " + purchaseUrl);
|
||||
return false;
|
||||
}
|
||||
@@ -194,13 +196,40 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
|
||||
SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link URL} from the given purchase URL String and user data, if it is valid.
|
||||
*
|
||||
* @param purchaseUrl The purchase URL String to use to create the URL.
|
||||
* @param userData The user data parameter from the entitlement server.
|
||||
* @param shouldAppendUserData If this is {@code true} and the {@code userData} exists,
|
||||
* the {@code userData} should be appended to the {@code purchaseUrl} to create the URL.
|
||||
* If this is false, only the {@code purchaseUrl} should be used and the {@code userData}
|
||||
* will be sent as data to the POST request instead.
|
||||
* @return The URL from the given purchase URL and user data or {@code null} if it is invalid.
|
||||
*/
|
||||
@Nullable public static URL getPurchaseUrl(@Nullable String purchaseUrl,
|
||||
@Nullable String userData, boolean shouldAppendUserData) {
|
||||
if (purchaseUrl == null) {
|
||||
return null;
|
||||
}
|
||||
// Only append user data if it exists, otherwise just return the purchase URL
|
||||
if (!shouldAppendUserData || TextUtils.isEmpty(userData)) {
|
||||
return getPurchaseUrl(purchaseUrl);
|
||||
}
|
||||
URL url = getPurchaseUrl(purchaseUrl + "?" + userData);
|
||||
if (url == null) {
|
||||
url = getPurchaseUrl(purchaseUrl);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link URL} from the given purchase URL String, if it is valid.
|
||||
*
|
||||
* @param purchaseUrl The purchase URL String to use to create the URL.
|
||||
* @return The purchase URL from the given String or {@code null} if it is invalid.
|
||||
*/
|
||||
@Nullable public static URL getPurchaseUrl(@Nullable String purchaseUrl) {
|
||||
@Nullable private static URL getPurchaseUrl(@Nullable String purchaseUrl) {
|
||||
if (!URLUtil.isValidUrl(purchaseUrl)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
@@ -33,6 +34,7 @@ import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.test.ActivityUnitTestCase;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
@@ -46,6 +48,8 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchaseActivity> {
|
||||
private static final String CARRIER = "Some Carrier";
|
||||
@@ -59,6 +63,7 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas
|
||||
@Mock CarrierConfigManager mCarrierConfigManager;
|
||||
@Mock NotificationManager mNotificationManager;
|
||||
@Mock PersistableBundle mPersistableBundle;
|
||||
@Mock WebView mWebView;
|
||||
|
||||
private SlicePurchaseActivity mSlicePurchaseActivity;
|
||||
private Context mContext;
|
||||
@@ -153,4 +158,23 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas
|
||||
mSlicePurchaseActivity.onDismissFlow();
|
||||
verify(mRequestFailedIntent).send();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartWebView() {
|
||||
// unspecified contents type
|
||||
SlicePurchaseActivity.startWebView(mWebView, URL, 0 /* CONTENTS_TYPE_UNSPECIFIED */, null);
|
||||
verify(mWebView).loadUrl(eq(URL));
|
||||
|
||||
// specified contents type with user data
|
||||
String userData = "userData";
|
||||
byte[] userDataBytes = userData.getBytes();
|
||||
SlicePurchaseActivity.startWebView(mWebView, URL, 1 /* CONTENTS_TYPE_JSON */, userData);
|
||||
verify(mWebView).postUrl(eq(URL), eq(userDataBytes));
|
||||
|
||||
// specified contents type with encoded user data
|
||||
byte[] encodedUserData = Base64.getEncoder().encode(userDataBytes);
|
||||
userData = "encodedValue=" + new String(encodedUserData);
|
||||
SlicePurchaseActivity.startWebView(mWebView, URL, 1 /* CONTENTS_TYPE_JSON */, userData);
|
||||
verify(mWebView, times(2)).postUrl(eq(URL), eq(userDataBytes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,14 +160,35 @@ public class SlicePurchaseBroadcastReceiverTest {
|
||||
"file:///android_asset/slice_store_test.html"
|
||||
};
|
||||
|
||||
// test invalid URLs
|
||||
for (String url : invalidUrls) {
|
||||
URL purchaseUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url);
|
||||
URL purchaseUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url, null, false);
|
||||
assertNull(purchaseUrl);
|
||||
}
|
||||
|
||||
// test asset URL
|
||||
assertEquals(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE,
|
||||
SlicePurchaseBroadcastReceiver.getPurchaseUrl(
|
||||
SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).toString());
|
||||
SlicePurchaseController.SLICE_PURCHASE_TEST_FILE, null, false).toString());
|
||||
|
||||
// test normal URL
|
||||
String validUrl = "http://www.google.com";
|
||||
assertEquals(validUrl,
|
||||
SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, null, false).toString());
|
||||
|
||||
// test normal URL with user data but no append
|
||||
String userData = "encryptedUserData=data";
|
||||
assertEquals(validUrl,
|
||||
SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, userData, false)
|
||||
.toString());
|
||||
|
||||
// test normal URL with user data and append
|
||||
assertEquals(validUrl + "?" + userData,
|
||||
SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, userData, true).toString());
|
||||
|
||||
// test normal URL without user data and append
|
||||
assertEquals(validUrl,
|
||||
SlicePurchaseBroadcastReceiver.getPurchaseUrl(validUrl, null, true).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user