summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sarah Chin <sarahchin@google.com> 2022-12-19 18:57:39 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-19 18:57:39 +0000
commit152f1c5c0fc8cde2ee38cc3cf0fc63753afe32e6 (patch)
treee899d085185649b0501ddbe925a5f75bb63ed1f0
parentef4cbddd832cf6fad8a4e372c47836739ebc57c0 (diff)
parentf09e5e1cc8795fd0542b321852abf5914ee7364e (diff)
Merge changes from topic "purchase_url"
* changes: Slice purchase app get URL from intent Slice purchase application update notification on locale change
-rw-r--r--packages/CarrierDefaultApp/AndroidManifest.xml1
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java68
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java168
-rw-r--r--packages/CarrierDefaultApp/tests/unit/Android.bp1
-rw-r--r--packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml2
-rw-r--r--packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java9
-rw-r--r--packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java200
7 files changed, 280 insertions, 169 deletions
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index c4bb17cb01dc..3f86aba26a1f 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -77,6 +77,7 @@
<receiver android:name="com.android.carrierdefaultapp.SlicePurchaseBroadcastReceiver"
android:exported="true">
<intent-filter>
+ <action android:name="android.intent.action.LOCALE_CHANGED" />
<action android:name="com.android.phone.slice.action.START_SLICE_PURCHASE_APP" />
<action android:name="com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT" />
<action android:name="com.android.phone.slice.action.NOTIFICATION_CANCELED" />
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
index c524037fe444..c08b83a7ae78 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
@@ -19,21 +19,17 @@ package com.android.carrierdefaultapp;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
-import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
-import android.webkit.URLUtil;
import android.webkit.WebView;
import com.android.phone.slice.SlicePurchaseController;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
@@ -62,38 +58,29 @@ public class SlicePurchaseActivity extends Activity {
@NonNull private WebView mWebView;
@NonNull private Context mApplicationContext;
@NonNull private Intent mIntent;
- @Nullable private URL mUrl;
- private int mSubId;
+ @NonNull private URL mUrl;
@TelephonyManager.PremiumCapability protected int mCapability;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIntent = getIntent();
- mSubId = mIntent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
+ int subId = mIntent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mCapability = mIntent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
+ String url = mIntent.getStringExtra(SlicePurchaseController.EXTRA_PURCHASE_URL);
mApplicationContext = getApplicationContext();
- mUrl = getUrl();
- logd("onCreate: subId=" + mSubId + ", capability="
- + TelephonyManager.convertPremiumCapabilityToString(mCapability)
- + ", url=" + mUrl);
+ logd("onCreate: subId=" + subId + ", capability="
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability) + ", url=" + url);
// Cancel network boost notification
- mApplicationContext.getSystemService(NotificationManager.class)
- .cancel(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability);
+ SlicePurchaseBroadcastReceiver.cancelNotification(mApplicationContext, mCapability);
- // Verify intent and values are valid
- if (!SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)) {
- loge("Not starting SlicePurchaseActivity with an invalid Intent: " + mIntent);
- SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
- mIntent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
- finishAndRemoveTask();
- return;
- }
+ // Verify purchase URL is valid
+ mUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url);
if (mUrl == null) {
- String error = "Unable to create a URL from carrier configs.";
+ String error = "Unable to create a purchase URL.";
loge(error);
Intent data = new Intent();
data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
@@ -104,18 +91,26 @@ public class SlicePurchaseActivity extends Activity {
finishAndRemoveTask();
return;
}
- if (mSubId != SubscriptionManager.getDefaultSubscriptionId()) {
+
+ // Verify intent is valid
+ if (!SlicePurchaseBroadcastReceiver.isIntentValid(mIntent)) {
+ loge("Not starting SlicePurchaseActivity with an invalid Intent: " + mIntent);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ mIntent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
+ finishAndRemoveTask();
+ return;
+ }
+
+ // Verify sub ID is valid
+ if (subId != SubscriptionManager.getDefaultSubscriptionId()) {
loge("Unable to start the slice purchase application on the non-default data "
- + "subscription: " + mSubId);
+ + "subscription: " + subId);
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
mIntent, SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION);
finishAndRemoveTask();
return;
}
- // Create a reference to this activity in SlicePurchaseBroadcastReceiver
- SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(mCapability, this);
-
// Create and configure WebView
setupWebView();
}
@@ -161,28 +156,9 @@ public class SlicePurchaseActivity extends Activity {
logd("onDestroy: User canceled the purchase by closing the application.");
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
mIntent, SlicePurchaseController.EXTRA_INTENT_CANCELED);
- SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(mCapability);
super.onDestroy();
}
- @Nullable private URL getUrl() {
- String url = mApplicationContext.getSystemService(CarrierConfigManager.class)
- .getConfigForSubId(mSubId).getString(
- CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
- boolean isUrlValid = URLUtil.isValidUrl(url);
- if (URLUtil.isAssetUrl(url)) {
- isUrlValid = url.equals(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
- }
- if (isUrlValid) {
- try {
- return new URL(url);
- } catch (MalformedURLException ignored) {
- }
- }
- loge("Invalid URL: " + url);
- return null;
- }
-
private void setupWebView() {
// Create WebView
mWebView = new WebView(this);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
index 367ae06adfc7..30411381ae82 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
@@ -16,6 +16,7 @@
package com.android.carrierdefaultapp;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
@@ -24,20 +25,28 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.drawable.Icon;
+import android.os.LocaleList;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.telephony.AnomalyReporter;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.webkit.URLUtil;
import android.webkit.WebView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.phone.slice.SlicePurchaseController;
-import java.lang.ref.WeakReference;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.HashMap;
+import java.util.Locale;
import java.util.Map;
import java.util.UUID;
@@ -57,10 +66,6 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
*/
private static final String UUID_BAD_PENDING_INTENT = "c360246e-95dc-4abf-9dc1-929a76cd7e53";
- /** Weak references to {@link SlicePurchaseActivity} for each capability, if it exists. */
- private static final Map<Integer, WeakReference<SlicePurchaseActivity>>
- sSlicePurchaseActivities = new HashMap<>();
-
/** Channel ID for the network boost notification. */
private static final String NETWORK_BOOST_NOTIFICATION_CHANNEL_ID = "network_boost";
/** Tag for the network boost notification. */
@@ -70,27 +75,28 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
"com.android.phone.slice.action.NOTIFICATION_CANCELED";
/**
- * Create a weak reference to {@link SlicePurchaseActivity}. The reference will be removed when
- * {@link SlicePurchaseActivity#onDestroy()} is called.
- *
- * @param capability The premium capability requested.
- * @param slicePurchaseActivity The instance of SlicePurchaseActivity.
+ * A map of Intents sent by {@link SlicePurchaseController} for each capability.
+ * If this map contains an Intent for a given capability, the network boost notification to
+ * purchase the capability is visible to the user.
+ * If this map does not contain an Intent for a given capability, either the capability was
+ * never requested or the {@link SlicePurchaseActivity} is visible to the user.
+ * An Intent is added to this map when the network boost notification is displayed to the user
+ * and removed from the map when the notification is canceled.
*/
- public static void updateSlicePurchaseActivity(
- @TelephonyManager.PremiumCapability int capability,
- @NonNull SlicePurchaseActivity slicePurchaseActivity) {
- sSlicePurchaseActivities.put(capability, new WeakReference<>(slicePurchaseActivity));
- }
+ private static final Map<Integer, Intent> sIntents = new HashMap<>();
/**
- * Remove the weak reference to {@link SlicePurchaseActivity} when
- * {@link SlicePurchaseActivity#onDestroy()} is called.
+ * Cancel the network boost notification for the given capability and
+ * remove the corresponding notification intent from the map.
*
- * @param capability The premium capability requested.
+ * @param context The context to cancel the notification in.
+ * @param capability The premium capability to cancel the notification for.
*/
- public static void removeSlicePurchaseActivity(
+ public static void cancelNotification(@NonNull Context context,
@TelephonyManager.PremiumCapability int capability) {
- sSlicePurchaseActivities.remove(capability);
+ context.getSystemService(NotificationManager.class).cancelAsUser(
+ NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
+ sIntents.remove(capability);
}
/**
@@ -139,7 +145,7 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
* Check whether the Intent is valid and can be used to complete purchases in the slice purchase
* application. This checks that all necessary extras exist and that the values are valid.
*
- * @param intent The intent to check
+ * @param intent The intent to check.
* @return {@code true} if the intent is valid and {@code false} otherwise.
*/
public static boolean isIntentValid(@NonNull Intent intent) {
@@ -164,6 +170,12 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
return false;
}
+ String purchaseUrl = intent.getStringExtra(SlicePurchaseController.EXTRA_PURCHASE_URL);
+ if (getPurchaseUrl(purchaseUrl) == null) {
+ loge("isIntentValid: invalid purchase URL: " + purchaseUrl);
+ return false;
+ }
+
String appName = intent.getStringExtra(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME);
if (TextUtils.isEmpty(appName)) {
loge("isIntentValid: empty requesting application name: " + appName);
@@ -180,6 +192,30 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
}
+ /**
+ * 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) {
+ if (!URLUtil.isValidUrl(purchaseUrl)) {
+ return null;
+ }
+ if (URLUtil.isAssetUrl(purchaseUrl)
+ && !purchaseUrl.equals(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE)) {
+ return null;
+ }
+ URL url = null;
+ try {
+ url = new URL(purchaseUrl);
+ url.toURI();
+ } catch (MalformedURLException | URISyntaxException e) {
+ loge("Invalid purchase URL: " + purchaseUrl + ", " + e);
+ }
+ return url;
+ }
+
private static boolean isPendingIntentValid(@NonNull Intent intent, @NonNull String extra) {
String intentType = getPendingIntentType(extra);
PendingIntent pendingIntent = intent.getParcelableExtra(extra, PendingIntent.class);
@@ -223,8 +259,11 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
logd("onReceive intent: " + intent.getAction());
switch (intent.getAction()) {
+ case Intent.ACTION_LOCALE_CHANGED:
+ onLocaleChanged(context);
+ break;
case SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP:
- onDisplayNetworkBoostNotification(context, intent);
+ onDisplayNetworkBoostNotification(context, intent, false);
break;
case SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT:
onTimeout(context, intent);
@@ -237,17 +276,31 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
}
}
+ private void onLocaleChanged(@NonNull Context context) {
+ if (sIntents.isEmpty()) return;
+
+ for (int capability : new int[]{TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY}) {
+ if (sIntents.get(capability) != null) {
+ // Notification is active -- update notification for new locale
+ context.getSystemService(NotificationManager.class).cancelAsUser(
+ NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
+ onDisplayNetworkBoostNotification(context, sIntents.get(capability), true);
+ }
+ }
+ }
+
private void onDisplayNetworkBoostNotification(@NonNull Context context,
- @NonNull Intent intent) {
- if (!isIntentValid(intent)) {
+ @NonNull Intent intent, boolean repeat) {
+ if (!repeat && !isIntentValid(intent)) {
sendSlicePurchaseAppResponse(intent,
SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
return;
}
+ Resources res = getResources(context);
NotificationChannel channel = new NotificationChannel(
NETWORK_BOOST_NOTIFICATION_CHANNEL_ID,
- context.getResources().getString(R.string.network_boost_notification_channel),
+ res.getString(R.string.network_boost_notification_channel),
NotificationManager.IMPORTANCE_DEFAULT);
// CarrierDefaultApp notifications are unblockable by default. Make this channel blockable
// to allow users to disable notifications posted to this channel without affecting other
@@ -257,12 +310,11 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
Notification notification =
new Notification.Builder(context, NETWORK_BOOST_NOTIFICATION_CHANNEL_ID)
- .setContentTitle(String.format(context.getResources().getString(
+ .setContentTitle(String.format(res.getString(
R.string.network_boost_notification_title),
intent.getStringExtra(
SlicePurchaseController.EXTRA_REQUESTING_APP_NAME)))
- .setContentText(context.getResources().getString(
- R.string.network_boost_notification_detail))
+ .setContentText(res.getString(R.string.network_boost_notification_detail))
.setSmallIcon(R.drawable.ic_network_boost)
.setContentIntent(createContentIntent(context, intent, 1))
.setDeleteIntent(intent.getParcelableExtra(
@@ -271,26 +323,56 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
// the user canceling or closing the notification.
.addAction(new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_network_boost),
- context.getResources().getString(
- R.string.network_boost_notification_button_not_now),
+ res.getString(R.string.network_boost_notification_button_not_now),
createCanceledIntent(context, intent)).build())
// Add an action for the "Manage" button, which has the same behavior as
// the user clicking on the notification.
.addAction(new Notification.Action.Builder(
Icon.createWithResource(context, R.drawable.ic_network_boost),
- context.getResources().getString(
- R.string.network_boost_notification_button_manage),
+ res.getString(R.string.network_boost_notification_button_manage),
createContentIntent(context, intent, 2)).build())
.build();
int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
- logd("Display the network boost notification for capability "
+ logd((repeat ? "Update" : "Display") + " the network boost notification for capability "
+ TelephonyManager.convertPremiumCapabilityToString(capability));
context.getSystemService(NotificationManager.class).notifyAsUser(
NETWORK_BOOST_NOTIFICATION_TAG, capability, notification, UserHandle.ALL);
- sendSlicePurchaseAppResponse(intent,
- SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
+ if (!repeat) {
+ sIntents.put(capability, intent);
+ sendSlicePurchaseAppResponse(intent,
+ SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN);
+ }
+ }
+
+ /**
+ * Get the {@link Resources} for the current locale.
+ *
+ * @param context The context to get the resources in.
+ *
+ * @return The resources in the current locale.
+ */
+ @VisibleForTesting
+ @NonNull public Resources getResources(@NonNull Context context) {
+ Resources resources = context.getResources();
+ Configuration config = resources.getConfiguration();
+ config.setLocale(getCurrentLocale());
+ return new Resources(resources.getAssets(), resources.getDisplayMetrics(), config);
+ }
+
+ /**
+ * Get the current {@link Locale} from the system property {@code persist.sys.locale}.
+ *
+ * @return The user's default/preferred language.
+ */
+ @VisibleForTesting
+ @NonNull public Locale getCurrentLocale() {
+ String languageTag = SystemProperties.get("persist.sys.locale");
+ if (TextUtils.isEmpty(languageTag)) {
+ return LocaleList.getAdjustedDefault().get(0);
+ }
+ return Locale.forLanguageTag(languageTag);
}
/**
@@ -343,16 +425,13 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("Purchase capability " + TelephonyManager.convertPremiumCapabilityToString(capability)
+ " timed out.");
- if (sSlicePurchaseActivities.get(capability) == null) {
- // Notification is still active
+ if (sIntents.get(capability) != null) {
+ // Notification is still active -- cancel pending notification
logd("Closing network boost notification since the user did not respond in time.");
- context.getSystemService(NotificationManager.class).cancelAsUser(
- NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
+ cancelNotification(context, capability);
} else {
- // Notification was dismissed but SlicePurchaseActivity is still active
- logd("Closing slice purchase application WebView since the user did not complete the "
- + "purchase in time.");
- sSlicePurchaseActivities.get(capability).get().finishAndRemoveTask();
+ // SlicePurchaseActivity is still active -- ignore timer
+ logd("Ignoring timeout since the SlicePurchaseActivity is still active.");
}
}
@@ -360,8 +439,7 @@ public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("onUserCanceled: " + TelephonyManager.convertPremiumCapabilityToString(capability));
- context.getSystemService(NotificationManager.class)
- .cancelAsUser(NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
+ cancelNotification(context, capability);
sendSlicePurchaseAppResponse(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED);
}
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.bp b/packages/CarrierDefaultApp/tests/unit/Android.bp
index cdf795752e07..0d08ec6ca2b3 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.bp
+++ b/packages/CarrierDefaultApp/tests/unit/Android.bp
@@ -37,5 +37,6 @@ android_test {
// Include all test java files.
srcs: ["src/**/*.java"],
platform_apis: true,
+ use_embedded_native_libs: false,
instrumentation_for: "CarrierDefaultApp",
}
diff --git a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
index 7a26d95551df..995170a61614 100644
--- a/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/tests/unit/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.carrierdefaultapp.tests.unit">
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
- <application>
+ <application android:extractNativeLibs="true">
<uses-library android:name="android.test.runner" />
</application>
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 cecc86d8e330..1bf644eb1fb6 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java
@@ -39,7 +39,6 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.phone.slice.SlicePurchaseController;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -94,6 +93,8 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas
SubscriptionManager.getDefaultDataSubscriptionId());
intent.putExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ intent.putExtra(SlicePurchaseController.EXTRA_PURCHASE_URL,
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE);
intent.putExtra(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME, TAG);
Intent spiedIntent = spy(intent);
@@ -110,12 +111,6 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas
mSlicePurchaseActivity = startActivity(spiedIntent, null, null);
}
- @After
- public void tearDown() throws Exception {
- mSlicePurchaseActivity.onDestroy();
- super.tearDown();
- }
-
@Test
public void testOnPurchaseSuccessful() throws Exception {
int duration = 5 * 60 * 1000; // 5 minutes
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 ab99a76902d9..958e13870759 100644
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
+++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiverTest.java
@@ -18,10 +18,14 @@ package com.android.carrierdefaultapp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
@@ -35,11 +39,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.UserHandle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.util.DisplayMetrics;
import androidx.test.runner.AndroidJUnit4;
@@ -52,6 +56,9 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.URL;
+import java.util.Locale;
+
@RunWith(AndroidJUnit4.class)
public class SlicePurchaseBroadcastReceiverTest {
private static final int PHONE_ID = 0;
@@ -67,24 +74,26 @@ public class SlicePurchaseBroadcastReceiverTest {
@Mock PendingIntent mNotificationShownIntent;
@Mock Context mContext;
@Mock Resources mResources;
+ @Mock Configuration mConfiguration;
@Mock NotificationManager mNotificationManager;
@Mock ApplicationInfo mApplicationInfo;
@Mock PackageManager mPackageManager;
- @Mock DisplayMetrics mDisplayMetrics;
- @Mock SlicePurchaseActivity mSlicePurchaseActivity;
private SlicePurchaseBroadcastReceiver mSlicePurchaseBroadcastReceiver;
- private ArgumentCaptor<Intent> mIntentCaptor;
- private ArgumentCaptor<Notification> mNotificationCaptor;
+ private Resources mSpiedResources;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mSpiedResources = spy(Resources.getSystem());
+
+ doReturn("").when(mResources).getString(anyInt());
doReturn(mNotificationManager).when(mContext)
.getSystemService(eq(NotificationManager.class));
+ doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mSpiedResources).when(mContext).getResources();
- mIntentCaptor = ArgumentCaptor.forClass(Intent.class);
- mNotificationCaptor = ArgumentCaptor.forClass(Notification.class);
mSlicePurchaseBroadcastReceiver = spy(new SlicePurchaseBroadcastReceiver());
}
@@ -109,8 +118,9 @@ public class SlicePurchaseBroadcastReceiverTest {
eq(EXTRA), eq(PendingIntent.class));
SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(
mContext, mIntent, EXTRA, mDataIntent);
- verify(mPendingIntent).send(eq(mContext), eq(0), mIntentCaptor.capture());
- assertEquals(mDataIntent, mIntentCaptor.getValue());
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mPendingIntent).send(eq(mContext), eq(0), captor.capture());
+ assertEquals(mDataIntent, captor.getValue());
}
@Test
@@ -124,6 +134,8 @@ public class SlicePurchaseBroadcastReceiverTest {
eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt());
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
+ doReturn(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).when(mIntent).getStringExtra(
+ eq(SlicePurchaseController.EXTRA_PURCHASE_URL));
doReturn(TAG).when(mIntent).getStringExtra(
eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME));
assertFalse(SlicePurchaseBroadcastReceiver.isIntentValid(mIntent));
@@ -137,18 +149,53 @@ public class SlicePurchaseBroadcastReceiverTest {
}
@Test
+ public void testGetPurchaseUrl() {
+ String[] invalidUrls = new String[] {
+ null,
+ "",
+ "www.google.com",
+ "htt://www.google.com",
+ "http//www.google.com",
+ "http:/www.google.com",
+ "file:///android_asset/",
+ "file:///android_asset/slice_store_test.html"
+ };
+
+ for (String url : invalidUrls) {
+ URL purchaseUrl = SlicePurchaseBroadcastReceiver.getPurchaseUrl(url);
+ assertNull(purchaseUrl);
+ }
+
+ assertEquals(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE,
+ SlicePurchaseBroadcastReceiver.getPurchaseUrl(
+ SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).toString());
+ }
+
+ @Test
public void testDisplayNetworkBoostNotification() throws Exception {
- // set up intent
- doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction();
- doReturn(PHONE_ID).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt());
- doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt());
- doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
- doReturn(TAG).when(mIntent).getStringExtra(
- eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME));
+ displayNetworkBoostNotification();
+ // verify network boost notification was shown
+ ArgumentCaptor<Notification> captor = ArgumentCaptor.forClass(Notification.class);
+ verify(mNotificationManager).notifyAsUser(
+ eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
+ eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
+ captor.capture(),
+ eq(UserHandle.ALL));
+
+ // verify notification fields
+ Notification notification = captor.getValue();
+ assertEquals(mContentIntent1, notification.contentIntent);
+ assertEquals(mPendingIntent, notification.deleteIntent);
+ assertEquals(2, notification.actions.length);
+ assertEquals(mCanceledIntent, notification.actions[0].actionIntent);
+ assertEquals(mContentIntent2, notification.actions[1].actionIntent);
+
+ // verify SlicePurchaseController was notified
+ verify(mNotificationShownIntent).send();
+ }
+
+ private void displayNetworkBoostNotification() {
// set up pending intents
doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mPendingIntent).getCreatorPackage();
doReturn(true).when(mPendingIntent).isBroadcast();
@@ -161,14 +208,7 @@ public class SlicePurchaseBroadcastReceiverTest {
eq(SlicePurchaseController.EXTRA_INTENT_NOTIFICATION_SHOWN),
eq(PendingIntent.class));
- // set up notification
- doReturn(mResources).when(mContext).getResources();
- doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
- doReturn("").when(mResources).getString(anyInt());
- doReturn(mApplicationInfo).when(mContext).getApplicationInfo();
- doReturn(mPackageManager).when(mContext).getPackageManager();
-
- // set up intents created by broadcast receiver
+ // spy notification intents to prevent PendingIntent issues
doReturn(mContentIntent1).when(mSlicePurchaseBroadcastReceiver).createContentIntent(
eq(mContext), eq(mIntent), eq(1));
doReturn(mContentIntent2).when(mSlicePurchaseBroadcastReceiver).createContentIntent(
@@ -176,35 +216,30 @@ public class SlicePurchaseBroadcastReceiverTest {
doReturn(mCanceledIntent).when(mSlicePurchaseBroadcastReceiver).createCanceledIntent(
eq(mContext), eq(mIntent));
+ // spy resources to prevent resource not found issues
+ doReturn(mResources).when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext));
+
// send ACTION_START_SLICE_PURCHASE_APP
+ doReturn(SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP).when(mIntent).getAction();
+ doReturn(PHONE_ID).when(mIntent).getIntExtra(
+ eq(SlicePurchaseController.EXTRA_PHONE_ID), anyInt());
+ doReturn(SubscriptionManager.getDefaultDataSubscriptionId()).when(mIntent).getIntExtra(
+ eq(SlicePurchaseController.EXTRA_SUB_ID), anyInt());
+ doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
+ eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
+ doReturn(SlicePurchaseController.SLICE_PURCHASE_TEST_FILE).when(mIntent).getStringExtra(
+ eq(SlicePurchaseController.EXTRA_PURCHASE_URL));
+ doReturn(TAG).when(mIntent).getStringExtra(
+ eq(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME));
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
-
- // verify network boost notification was shown
- verify(mNotificationManager).notifyAsUser(
- eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
- eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
- mNotificationCaptor.capture(),
- eq(UserHandle.ALL));
-
- Notification notification = mNotificationCaptor.getValue();
- assertEquals(mContentIntent1, notification.contentIntent);
- assertEquals(mPendingIntent, notification.deleteIntent);
- assertEquals(2, notification.actions.length);
- assertEquals(mCanceledIntent, notification.actions[0].actionIntent);
- assertEquals(mContentIntent2, notification.actions[1].actionIntent);
-
- // verify SlicePurchaseController was notified
- verify(mNotificationShownIntent).send();
}
@Test
public void testNotificationCanceled() {
- // set up intent
+ // send ACTION_NOTIFICATION_CANCELED
doReturn("com.android.phone.slice.action.NOTIFICATION_CANCELED").when(mIntent).getAction();
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
-
- // send ACTION_NOTIFICATION_CANCELED
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
@@ -215,14 +250,14 @@ public class SlicePurchaseBroadcastReceiverTest {
}
@Test
- public void testNotificationTimeout() {
- // set up intent
+ public void testNotificationTimeout() throws Exception {
+ displayNetworkBoostNotification();
+
+ // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent)
.getAction();
doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
-
- // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
// verify notification was canceled
@@ -233,27 +268,52 @@ public class SlicePurchaseBroadcastReceiverTest {
}
@Test
- // TODO: WebView/Activity should not close on timeout.
- // This test should be removed once implementation is fixed.
- public void testActivityTimeout() {
- // create and track activity
- SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(
- TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY, mSlicePurchaseActivity);
-
- // set up intent
- doReturn(SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT).when(mIntent)
- .getAction();
- doReturn(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY).when(mIntent).getIntExtra(
- eq(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY), anyInt());
-
- // send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
+ public void testLocaleChanged() throws Exception {
+ // get previous locale
+ doReturn(mConfiguration).when(mSpiedResources).getConfiguration();
+ Locale before = getLocale();
+
+ // display notification
+ displayNetworkBoostNotification();
+ clearInvocations(mNotificationManager);
+ clearInvocations(mNotificationShownIntent);
+
+ // change current locale from previous value
+ Locale newLocale = Locale.forLanguageTag("en-US");
+ if (before.equals(newLocale)) {
+ newLocale = Locale.forLanguageTag("ko-KR");
+ }
+ doReturn(newLocale).when(mSlicePurchaseBroadcastReceiver).getCurrentLocale();
+
+ // send ACTION_LOCALE_CHANGED
+ doReturn(Intent.ACTION_LOCALE_CHANGED).when(mIntent).getAction();
mSlicePurchaseBroadcastReceiver.onReceive(mContext, mIntent);
- // verify activity was canceled
- verify(mSlicePurchaseActivity).finishAndRemoveTask();
+ // verify notification was updated and SlicePurchaseController was not notified
+ verify(mNotificationManager).cancelAsUser(
+ eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
+ eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
+ eq(UserHandle.ALL));
+ verify(mNotificationManager).notifyAsUser(
+ eq(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG),
+ eq(TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY),
+ any(Notification.class),
+ eq(UserHandle.ALL));
+ verify(mNotificationShownIntent, never()).send();
+
+ // verify locale was changed successfully
+ doCallRealMethod().when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext));
+ assertEquals(newLocale, getLocale());
+ }
- // untrack activity
- SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(
- TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
+ private Locale getLocale() {
+ try {
+ mSlicePurchaseBroadcastReceiver.getResources(mContext);
+ fail("getLocale should not have completed successfully.");
+ } catch (NullPointerException expected) { }
+ ArgumentCaptor<Locale> captor = ArgumentCaptor.forClass(Locale.class);
+ verify(mConfiguration).setLocale(captor.capture());
+ clearInvocations(mConfiguration);
+ return captor.getValue();
}
}