Merge changes from topic "purchase_url"
* changes: Slice purchase app get URL from intent Slice purchase application update notification on locale change
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 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));
|
||||
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 {
|
||||
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);
|
||||
public void testLocaleChanged() throws Exception {
|
||||
// get previous locale
|
||||
doReturn(mConfiguration).when(mSpiedResources).getConfiguration();
|
||||
Locale before = getLocale();
|
||||
|
||||
// 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());
|
||||
// display notification
|
||||
displayNetworkBoostNotification();
|
||||
clearInvocations(mNotificationManager);
|
||||
clearInvocations(mNotificationShownIntent);
|
||||
|
||||
// send ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT
|
||||
// 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();
|
||||
|
||||
// untrack activity
|
||||
SlicePurchaseBroadcastReceiver.removeSlicePurchaseActivity(
|
||||
TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY);
|
||||
// verify locale was changed successfully
|
||||
doCallRealMethod().when(mSlicePurchaseBroadcastReceiver).getResources(eq(mContext));
|
||||
assertEquals(newLocale, getLocale());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user