From 3f496c3f0fabe708f69b0f43ae1499f0a1146c83 Mon Sep 17 00:00:00 2001 From: Sarah Chin Date: Thu, 11 May 2023 23:17:08 -0700 Subject: [PATCH] SlicePurchaseActivity handle user data based on contents type If contents type is not present, we need to append user data to the url and send it as a GET request. If contents type is xml or json, we need to send the user data as a POST request. If user data is encoded, we need to decode it before sending it in the POST request. If the contents type is specified but user data does not exist, return an error. Test: atest CarrierDefaultAppUnitTests Test: manual test userdata is properly appended or posted Bug: 282905562 Change-Id: I59e1e28e7e1bd583307da55187886b8bb0798006 Merged-In: I59e1e28e7e1bd583307da55187886b8bb0798006 --- .../SlicePurchaseActivity.java | 85 ++++++++++++++++--- .../SlicePurchaseBroadcastReceiver.java | 33 ++++++- .../SlicePurchaseActivityTest.java | 24 ++++++ .../SlicePurchaseBroadcastReceiverTest.java | 25 +++++- 4 files changed, 152 insertions(+), 15 deletions(-) diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java index b1009808ccccb..fcc4ec1228390 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java @@ -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: + * + * @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); } } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java index 23b976638eef7..9b33704cc8e7e 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java @@ -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; } diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java index cc103fa98e654..1ec180beea819 100644 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java +++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java @@ -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 { private static final String CARRIER = "Some Carrier"; @@ -59,6 +63,7 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase