diff options
author | 2023-05-11 23:17:08 -0700 | |
---|---|---|
committer | 2023-07-06 15:12:17 -0700 | |
commit | 360c235ef631ab3c04dd68175d1336dbb4466da9 (patch) | |
tree | 7ea1144f23926e02b8cd3d1bbf7093a37643fcc0 | |
parent | 7e1bfdf07b567277a336db1b0e2163c65fbb8549 (diff) |
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
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 b1009808cccc..fcc4ec122839 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: + * <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); } } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java index 23b976638eef..9b33704cc8e7 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; } @@ -195,12 +197,39 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{ } /** + * 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 cc103fa98e65..1ec180beea81 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<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)); + } } diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java index 952789c56b2e..61847b517c8d 100644 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java +++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java @@ -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 |