Resolve API feedback and remove references to SliceStore
Rename PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA to
PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUB
Split KEY_PREMIUM_CAPABILITY_MAXIMUM_NOTIFICATION_COUNT_INT_ARRAY
into KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT
and KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT
Rename SliceStore to SlicePurchaseController. Remove all references to
SliceStore.
Test: atest TelephonyManagerReadNonDangerousPermissionTest
Test: manually verify premium capability purchase behavior
Bug: 255552152
Change-Id: I653db7566c91942faadcec24fb7bb340236ebdc0
diff --git a/packages/CarrierDefaultApp/Android.bp b/packages/CarrierDefaultApp/Android.bp
index 62ffe38..a216381 100644
--- a/packages/CarrierDefaultApp/Android.bp
+++ b/packages/CarrierDefaultApp/Android.bp
@@ -10,7 +10,7 @@
android_app {
name: "CarrierDefaultApp",
srcs: ["src/**/*.java"],
- libs: ["SliceStore"],
+ libs: ["SlicePurchaseController"],
platform_apis: true,
certificate: "platform",
optimize: {
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index a5b104b..4c22ee6 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -73,16 +73,16 @@
</intent-filter>
</activity-alias>
- <receiver android:name="com.android.carrierdefaultapp.SliceStoreBroadcastReceiver"
+ <receiver android:name="com.android.carrierdefaultapp.SlicePurchaseBroadcastReceiver"
android:exported="true">
<intent-filter>
- <action android:name="com.android.phone.slicestore.action.START_SLICE_STORE" />
- <action android:name="com.android.phone.slicestore.action.SLICE_STORE_RESPONSE_TIMEOUT" />
- <action android:name="com.android.phone.slicestore.action.NOTIFICATION_CANCELED" />
+ <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" />
</intent-filter>
</receiver>
- <activity android:name="com.android.carrierdefaultapp.SliceStoreActivity"
- android:label="@string/slice_store_label"
+ <activity android:name="com.android.carrierdefaultapp.SlicePurchaseActivity"
+ android:label="@string/slice_purchase_app_label"
android:exported="true"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
diff --git a/packages/CarrierDefaultApp/assets/slice_store_test.html b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
similarity index 86%
rename from packages/CarrierDefaultApp/assets/slice_store_test.html
rename to packages/CarrierDefaultApp/assets/slice_purchase_test.html
index 7ddbd2d..67d2184 100644
--- a/packages/CarrierDefaultApp/assets/slice_store_test.html
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.html
@@ -20,7 +20,8 @@
<meta charset="UTF-8">
<meta name="description" content="
This is a HTML page that calls and verifies responses from the @JavascriptInterface functions of
- SliceStoreWebInterface. Test SliceStore APIs using ADB shell commands and the APIs below:
+ SlicePurchaseWebInterface. Test slice purchase application behavior using ADB shell commands and
+ the APIs below:
FROM TERMINAL:
Allow device to override carrier configs:
@@ -29,7 +30,7 @@
$ adb shell cmd phone cc set-value -p supported_premium_capabilities_int_array 34
Set the carrier purchase URL to this test HTML file:
$ adb shell cmd phone cc set-value -p premium_capability_purchase_url_string \
- file:///android_asset/slice_store_test.html
+ file:///android_asset/slice_purchase_test.html
OPTIONAL: Allow premium capability purchase on LTE:
$ adb shell cmd phone cc set-value -p premium_capability_supported_on_lte_bool true
OPTIONAL: Override ServiceState to fake a NR SA connection:
@@ -43,7 +44,7 @@
this.getMainExecutor(), request::offer);
When the test application starts, this HTML will be loaded into the WebView along with the
- associated JavaScript functions in file:///android_asset/slice_store_test.js.
+ associated JavaScript functions in file:///android_asset/slice_purchase_test.js.
Click on the buttons in the HTML to call the corresponding @JavascriptInterface APIs.
RESET DEVICE STATE:
@@ -52,11 +53,11 @@
Clear ServiceState override that was set:
$ adb shell am broadcast -a com.android.internal.telephony.TestServiceState --es action reset
">
- <title>Test SliceStoreActivity</title>
- <script type="text/javascript" src="slice_store_test.js"></script>
+ <title>Test SlicePurchaseActivity</title>
+ <script type="text/javascript" src="slice_purchase_test.js"></script>
</head>
<body>
- <h1>Test SliceStoreActivity</h1>
+ <h1>Test SlicePurchaseActivity</h1>
<h2>Get requested premium capability</h2>
<button type="button" onclick="testGetRequestedCapability()">
Get requested premium capability
@@ -70,7 +71,7 @@
<p id="purchase_successful"></p>
<h2>Notify purchase failed</h2>
- <button type="button" onclick="testNotifyPurchaseFailed()">
+ <button type="button" onclick="testNotifyPurchaseFailed(2, 'FAILURE_CODE_SERVER_UNREACHABLE')">
Notify purchase failed
</button>
<p id="purchase_failed"></p>
diff --git a/packages/CarrierDefaultApp/assets/slice_store_test.js b/packages/CarrierDefaultApp/assets/slice_purchase_test.js
similarity index 76%
rename from packages/CarrierDefaultApp/assets/slice_store_test.js
rename to packages/CarrierDefaultApp/assets/slice_purchase_test.js
index f12a6da..02c4fea 100644
--- a/packages/CarrierDefaultApp/assets/slice_store_test.js
+++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.js
@@ -15,19 +15,19 @@
*/
function testGetRequestedCapability() {
- let capability = SliceStoreWebInterface.getRequestedCapability();
+ let capability = SlicePurchaseWebInterface.getRequestedCapability();
document.getElementById("requested_capability").innerHTML =
"Premium capability requested: " + capability;
}
function testNotifyPurchaseSuccessful(duration_ms_long = 0) {
- SliceStoreWebInterface.notifyPurchaseSuccessful(duration);
+ SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long);
document.getElementById("purchase_successful").innerHTML =
- "Notified purchase success for duration: " + duration;
+ "Notified purchase success for duration: " + duration_ms_long;
}
-function testNotifyPurchaseFailed() {
- SliceStoreWebInterface.notifyPurchaseFailed();
+function testNotifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") {
+ SlicePurchaseWebInterface.notifyPurchaseFailed(failure_code, failure_reason);
document.getElementById("purchase_failed").innerHTML =
"Notified purchase failed.";
}
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index ce88a40..8a19709 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -25,6 +25,6 @@
<!-- Notification button text to manage the network boost notification. -->
<string name="network_boost_notification_button_manage">Manage</string>
- <!-- Label to display when the slice store opens. -->
- <string name="slice_store_label">Purchase a network boost.</string>
+ <!-- Label to display when the slice purchase application opens. -->
+ <string name="slice_purchase_app_label">Purchase a network boost.</string>
</resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
new file mode 100644
index 0000000..e67ea7e
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+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.WebView;
+
+import com.android.phone.slice.SlicePurchaseController;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Activity that launches when the user clicks on the network boost notification.
+ * This will open a {@link WebView} for the carrier website to allow the user to complete the
+ * premium capability purchase.
+ * The carrier website can get the requested premium capability using the JavaScript interface
+ * method {@code SlicePurchaseWebInterface.getRequestedCapability()}.
+ * If the purchase is successful, the carrier website shall notify the slice purchase application
+ * using the JavaScript interface method
+ * {@code SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration)}, where {@code duration} is
+ * the optional duration of the network boost.
+ * If the purchase was not successful, the carrier website shall notify the slice purchase
+ * application using the JavaScript interface method
+ * {@code SlicePurchaseWebInterface.notifyPurchaseFailed(code, reason)}, where {@code code} is the
+ * {@link SlicePurchaseController.FailureCode} indicating the reason for failure and {@code reason}
+ * is the human-readable reason for failure if the failure code is
+ * {@link SlicePurchaseController#FAILURE_CODE_UNKNOWN}.
+ * If either of these notification methods are not called, the purchase cannot be completed
+ * successfully and the purchase request will eventually time out.
+ */
+public class SlicePurchaseActivity extends Activity {
+ private static final String TAG = "SlicePurchaseActivity";
+
+ private @NonNull WebView mWebView;
+ private @NonNull Context mApplicationContext;
+ private int mSubId;
+ @TelephonyManager.PremiumCapability protected int mCapability;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ mSubId = intent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ mCapability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
+ mApplicationContext = getApplicationContext();
+ URL url = getUrl();
+ logd("onCreate: subId=" + mSubId + ", capability="
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + ", url=" + url);
+
+ // Cancel network boost notification
+ mApplicationContext.getSystemService(NotificationManager.class)
+ .cancel(SlicePurchaseBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability);
+
+ // Verify intent and values are valid
+ if (!SlicePurchaseBroadcastReceiver.isIntentValid(intent)) {
+ loge("Not starting SlicePurchaseActivity with an invalid Intent: " + intent);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
+ finishAndRemoveTask();
+ return;
+ }
+ if (url == null) {
+ String error = "Unable to create a URL from carrier configs.";
+ loge(error);
+ Intent data = new Intent();
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE,
+ SlicePurchaseController.FAILURE_CODE_CARRIER_URL_UNAVAILABLE);
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_REASON, error);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR, data);
+ finishAndRemoveTask();
+ return;
+ }
+ if (mSubId != SubscriptionManager.getDefaultSubscriptionId()) {
+ loge("Unable to start the slice purchase application on the non-default data "
+ + "subscription: " + mSubId);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ intent, SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB);
+ finishAndRemoveTask();
+ return;
+ }
+
+ // Create a reference to this activity in SlicePurchaseBroadcastReceiver
+ SlicePurchaseBroadcastReceiver.updateSlicePurchaseActivity(mCapability, this);
+
+ // Create and configure WebView
+ mWebView = new WebView(this);
+ // Enable JavaScript for the carrier purchase website to send results back to
+ // the slice purchase application.
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.addJavascriptInterface(
+ new SlicePurchaseWebInterface(this), "SlicePurchaseWebInterface");
+
+ // Display WebView
+ setContentView(mWebView);
+ mWebView.loadUrl(url.toString());
+ }
+
+ protected void onPurchaseSuccessful(long duration) {
+ logd("onPurchaseSuccessful: Carrier website indicated successfully purchased premium "
+ + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability)
+ + (duration > 0 ? " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes."
+ : "."));
+ Intent intent = new Intent();
+ intent.putExtra(SlicePurchaseController.EXTRA_PURCHASE_DURATION, duration);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_SUCCESS, intent);
+ finishAndRemoveTask();
+ }
+
+ protected void onPurchaseFailed(@SlicePurchaseController.FailureCode int failureCode,
+ @Nullable String failureReason) {
+ logd("onPurchaseFailed: Carrier website indicated purchase failed for premium capability "
+ + TelephonyManager.convertPremiumCapabilityToString(mCapability) + " with code: "
+ + failureCode + " and reason: " + failureReason);
+ Intent data = new Intent();
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_CODE, failureCode);
+ data.putExtra(SlicePurchaseController.EXTRA_FAILURE_REASON, failureReason);
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext,
+ getIntent(), SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR, data);
+ finishAndRemoveTask();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
+ // Pressing back in the WebView will go to the previous page instead of closing
+ // the slice purchase application.
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
+ mWebView.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onDestroy() {
+ logd("onDestroy: User canceled the purchase by closing the application.");
+ SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse(
+ getIntent(), 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);
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ loge("Invalid URL: " + url);
+ }
+ return null;
+ }
+
+ private static void logd(@NonNull String s) {
+ Log.d(TAG, s);
+ }
+
+ private static void loge(@NonNull String s) {
+ Log.e(TAG, s);
+ }
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
similarity index 68%
rename from packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java
rename to packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
index 7867ef1..5761b3c 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseBroadcastReceiver.java
@@ -33,7 +33,7 @@
import android.util.Log;
import android.webkit.WebView;
-import com.android.phone.slicestore.SliceStore;
+import com.android.phone.slice.SlicePurchaseController;
import java.lang.ref.WeakReference;
import java.util.HashMap;
@@ -41,13 +41,14 @@
import java.util.UUID;
/**
- * The SliceStoreBroadcastReceiver listens for {@link SliceStore#ACTION_START_SLICE_STORE} from the
- * SliceStore in the phone process to start the SliceStore application. It displays the network
- * boost notification to the user and will start the {@link SliceStoreActivity} to display the
+ * The SlicePurchaseBroadcastReceiver listens for
+ * {@link SlicePurchaseController#ACTION_START_SLICE_PURCHASE_APP} from the SlicePurchaseController
+ * in the phone process to start the slice purchase application. It displays the network boost
+ * notification to the user and will start the {@link SlicePurchaseActivity} to display the
* {@link WebView} to purchase network boosts from the user's carrier.
*/
-public class SliceStoreBroadcastReceiver extends BroadcastReceiver{
- private static final String TAG = "SliceStoreBroadcastReceiver";
+public class SlicePurchaseBroadcastReceiver extends BroadcastReceiver{
+ private static final String TAG = "SlicePurchaseBroadcastReceiver";
/**
* UUID to report an anomaly when receiving a PendingIntent from an application or process
@@ -55,48 +56,49 @@
*/
private static final String UUID_BAD_PENDING_INTENT = "c360246e-95dc-4abf-9dc1-929a76cd7e53";
- /** Weak references to {@link SliceStoreActivity} for each capability, if it exists. */
- private static final Map<Integer, WeakReference<SliceStoreActivity>> sSliceStoreActivities =
- new HashMap<>();
+ /** 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. */
- public static final String NETWORK_BOOST_NOTIFICATION_TAG = "SliceStore.Notification";
+ public static final String NETWORK_BOOST_NOTIFICATION_TAG = "SlicePurchaseApp.Notification";
/** Action for when the user clicks the "Not now" button on the network boost notification. */
private static final String ACTION_NOTIFICATION_CANCELED =
- "com.android.phone.slicestore.action.NOTIFICATION_CANCELED";
+ "com.android.phone.slice.action.NOTIFICATION_CANCELED";
/**
- * Create a weak reference to {@link SliceStoreActivity}. The reference will be removed when
- * {@link SliceStoreActivity#onDestroy()} is called.
+ * 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 sliceStoreActivity The instance of SliceStoreActivity.
+ * @param slicePurchaseActivity The instance of SlicePurchaseActivity.
*/
- public static void updateSliceStoreActivity(@TelephonyManager.PremiumCapability int capability,
- @NonNull SliceStoreActivity sliceStoreActivity) {
- sSliceStoreActivities.put(capability, new WeakReference<>(sliceStoreActivity));
+ public static void updateSlicePurchaseActivity(
+ @TelephonyManager.PremiumCapability int capability,
+ @NonNull SlicePurchaseActivity slicePurchaseActivity) {
+ sSlicePurchaseActivities.put(capability, new WeakReference<>(slicePurchaseActivity));
}
/**
- * Remove the weak reference to {@link SliceStoreActivity} when
- * {@link SliceStoreActivity#onDestroy()} is called.
+ * Remove the weak reference to {@link SlicePurchaseActivity} when
+ * {@link SlicePurchaseActivity#onDestroy()} is called.
*
* @param capability The premium capability requested.
*/
- public static void removeSliceStoreActivity(
+ public static void removeSlicePurchaseActivity(
@TelephonyManager.PremiumCapability int capability) {
- sSliceStoreActivities.remove(capability);
+ sSlicePurchaseActivities.remove(capability);
}
/**
- * Send the PendingIntent containing the corresponding SliceStore response.
+ * Send the PendingIntent containing the corresponding slice purchase application response.
*
* @param intent The Intent containing the PendingIntent extra.
* @param extra The extra to get the PendingIntent to send.
*/
- public static void sendSliceStoreResponse(@NonNull Intent intent, @NonNull String extra) {
+ public static void sendSlicePurchaseAppResponse(@NonNull Intent intent, @NonNull String extra) {
PendingIntent pendingIntent = intent.getParcelableExtra(extra, PendingIntent.class);
if (pendingIntent == null) {
loge("PendingIntent does not exist for extra: " + extra);
@@ -110,14 +112,15 @@
}
/**
- * Send the PendingIntent containing the corresponding SliceStore response with additional data.
+ * Send the PendingIntent containing the corresponding slice purchase application response
+ * with additional data.
*
* @param context The Context to use to send the PendingIntent.
* @param intent The Intent containing the PendingIntent extra.
* @param extra The extra to get the PendingIntent to send.
* @param data The Intent containing additional data to send with the PendingIntent.
*/
- public static void sendSliceStoreResponseWithData(@NonNull Context context,
+ public static void sendSlicePurchaseAppResponseWithData(@NonNull Context context,
@NonNull Intent intent, @NonNull String extra, @NonNull Intent data) {
PendingIntent pendingIntent = intent.getParcelableExtra(extra, PendingIntent.class);
if (pendingIntent == null) {
@@ -132,45 +135,46 @@
}
/**
- * Check whether the Intent is valid and can be used to complete purchases in the SliceStore.
- * This checks that all necessary extras exist and that the values are valid.
+ * 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
* @return {@code true} if the intent is valid and {@code false} otherwise.
*/
public static boolean isIntentValid(@NonNull Intent intent) {
- int phoneId = intent.getIntExtra(SliceStore.EXTRA_PHONE_ID,
+ int phoneId = intent.getIntExtra(SlicePurchaseController.EXTRA_PHONE_ID,
SubscriptionManager.INVALID_PHONE_INDEX);
if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
loge("isIntentValid: invalid phone index: " + phoneId);
return false;
}
- int subId = intent.getIntExtra(SliceStore.EXTRA_SUB_ID,
+ int subId = intent.getIntExtra(SlicePurchaseController.EXTRA_SUB_ID,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
loge("isIntentValid: invalid subscription ID: " + subId);
return false;
}
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
- if (capability == SliceStore.PREMIUM_CAPABILITY_INVALID) {
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
+ if (capability == SlicePurchaseController.PREMIUM_CAPABILITY_INVALID) {
loge("isIntentValid: invalid premium capability: " + capability);
return false;
}
- String appName = intent.getStringExtra(SliceStore.EXTRA_REQUESTING_APP_NAME);
+ String appName = intent.getStringExtra(SlicePurchaseController.EXTRA_REQUESTING_APP_NAME);
if (TextUtils.isEmpty(appName)) {
loge("isIntentValid: empty requesting application name: " + appName);
return false;
}
- return isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_CANCELED)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_CARRIER_ERROR)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA)
- && isPendingIntentValid(intent, SliceStore.EXTRA_INTENT_SUCCESS);
+ return isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED)
+ && isPendingIntentValid(intent,
+ SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB)
+ && isPendingIntentValid(intent, SlicePurchaseController.EXTRA_INTENT_SUCCESS);
}
private static boolean isPendingIntentValid(@NonNull Intent intent, @NonNull String extra) {
@@ -197,11 +201,12 @@
@NonNull private static String getPendingIntentType(@NonNull String extra) {
switch (extra) {
- case SliceStore.EXTRA_INTENT_CANCELED: return "canceled";
- case SliceStore.EXTRA_INTENT_CARRIER_ERROR: return "carrier error";
- case SliceStore.EXTRA_INTENT_REQUEST_FAILED: return "request failed";
- case SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA: return "not default data";
- case SliceStore.EXTRA_INTENT_SUCCESS: return "success";
+ case SlicePurchaseController.EXTRA_INTENT_CANCELED: return "canceled";
+ case SlicePurchaseController.EXTRA_INTENT_CARRIER_ERROR: return "carrier error";
+ case SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED: return "request failed";
+ case SlicePurchaseController.EXTRA_INTENT_NOT_DEFAULT_DATA_SUB:
+ return "not default data sub";
+ case SlicePurchaseController.EXTRA_INTENT_SUCCESS: return "success";
default: {
loge("Unknown pending intent extra: " + extra);
return "unknown(" + extra + ")";
@@ -213,10 +218,10 @@
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
logd("onReceive intent: " + intent.getAction());
switch (intent.getAction()) {
- case SliceStore.ACTION_START_SLICE_STORE:
+ case SlicePurchaseController.ACTION_START_SLICE_PURCHASE_APP:
onDisplayBoosterNotification(context, intent);
break;
- case SliceStore.ACTION_SLICE_STORE_RESPONSE_TIMEOUT:
+ case SlicePurchaseController.ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT:
onTimeout(context, intent);
break;
case ACTION_NOTIFICATION_CANCELED:
@@ -229,7 +234,8 @@
private void onDisplayBoosterNotification(@NonNull Context context, @NonNull Intent intent) {
if (!isIntentValid(intent)) {
- sendSliceStoreResponse(intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED);
+ sendSlicePurchaseAppResponse(intent,
+ SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED);
return;
}
@@ -243,13 +249,14 @@
new Notification.Builder(context, NETWORK_BOOST_NOTIFICATION_CHANNEL_ID)
.setContentTitle(String.format(context.getResources().getString(
R.string.network_boost_notification_title),
- intent.getStringExtra(SliceStore.EXTRA_REQUESTING_APP_NAME)))
+ intent.getStringExtra(
+ SlicePurchaseController.EXTRA_REQUESTING_APP_NAME)))
.setContentText(context.getResources().getString(
R.string.network_boost_notification_detail))
.setSmallIcon(R.drawable.ic_network_boost)
.setContentIntent(createContentIntent(context, intent, 1))
.setDeleteIntent(intent.getParcelableExtra(
- SliceStore.EXTRA_INTENT_CANCELED, PendingIntent.class))
+ SlicePurchaseController.EXTRA_INTENT_CANCELED, PendingIntent.class))
// Add an action for the "Not now" button, which has the same behavior as
// the user canceling or closing the notification.
.addAction(new Notification.Action.Builder(
@@ -266,8 +273,8 @@
createContentIntent(context, intent, 2)).build())
.build();
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("Display the booster notification for capability "
+ TelephonyManager.convertPremiumCapabilityToString(capability));
context.getSystemService(NotificationManager.class).notifyAsUser(
@@ -276,19 +283,19 @@
/**
* Create the intent for when the user clicks on the "Manage" button on the network boost
- * notification or the notification itself. This will open {@link SliceStoreActivity}.
+ * notification or the notification itself. This will open {@link SlicePurchaseActivity}.
*
* @param context The Context to create the intent for.
- * @param intent The source Intent used to launch the SliceStore application.
+ * @param intent The source Intent used to launch the slice purchase application.
* @param requestCode The request code for the PendingIntent.
*
- * @return The intent to start {@link SliceStoreActivity}.
+ * @return The intent to start {@link SlicePurchaseActivity}.
*/
@NonNull private PendingIntent createContentIntent(@NonNull Context context,
@NonNull Intent intent, int requestCode) {
- Intent i = new Intent(context, SliceStoreActivity.class);
+ Intent i = new Intent(context, SlicePurchaseActivity.class);
i.setComponent(ComponentName.unflattenFromString(
- "com.android.carrierdefaultapp/.SliceStoreActivity"));
+ "com.android.carrierdefaultapp/.SlicePurchaseActivity"));
i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
| Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
i.putExtras(intent);
@@ -303,7 +310,7 @@
* as if the user had canceled or removed the notification.
*
* @param context The Context to create the intent for.
- * @param intent The source Intent used to launch the SliceStore application.
+ * @param intent The source Intent used to launch the slice purchase application.
*
* @return The canceled intent.
*/
@@ -311,37 +318,37 @@
@NonNull Intent intent) {
Intent i = new Intent(ACTION_NOTIFICATION_CANCELED);
i.setComponent(ComponentName.unflattenFromString(
- "com.android.carrierdefaultapp/.SliceStoreBroadcastReceiver"));
+ "com.android.carrierdefaultapp/.SlicePurchaseBroadcastReceiver"));
i.putExtras(intent);
return PendingIntent.getBroadcast(context, 0, i,
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
}
private void onTimeout(@NonNull Context context, @NonNull Intent intent) {
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ int capability = intent.getIntExtra(SlicePurchaseController.EXTRA_PREMIUM_CAPABILITY,
+ SlicePurchaseController.PREMIUM_CAPABILITY_INVALID);
logd("Purchase capability " + TelephonyManager.convertPremiumCapabilityToString(capability)
+ " timed out.");
- if (sSliceStoreActivities.get(capability) == null) {
+ if (sSlicePurchaseActivities.get(capability) == null) {
// Notification is still active
logd("Closing booster notification since the user did not respond in time.");
context.getSystemService(NotificationManager.class).cancelAsUser(
NETWORK_BOOST_NOTIFICATION_TAG, capability, UserHandle.ALL);
} else {
- // Notification was dismissed but SliceStoreActivity is still active
- logd("Closing SliceStore WebView since the user did not complete the purchase "
- + "in time.");
- sSliceStoreActivities.get(capability).get().finishAndRemoveTask();
+ // 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();
}
}
private void onUserCanceled(@NonNull Context context, @NonNull Intent intent) {
- int capability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
+ 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);
- sendSliceStoreResponse(intent, SliceStore.EXTRA_INTENT_CANCELED);
+ sendSlicePurchaseAppResponse(intent, SlicePurchaseController.EXTRA_INTENT_CANCELED);
}
private static void logd(String s) {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
similarity index 67%
rename from packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java
rename to packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
index ab5d080..8547898 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreWebInterface.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
@@ -21,18 +21,19 @@
import android.telephony.TelephonyManager;
import android.webkit.JavascriptInterface;
-import com.android.phone.slicestore.SliceStore;
+import com.android.phone.slice.SlicePurchaseController;
/**
- * SliceStore web interface class allowing carrier websites to send responses back to SliceStore
- * using JavaScript.
+ * Slice purchase web interface class allowing carrier websites to send responses back to the
+ * slice purchase application using JavaScript.
*/
-public class SliceStoreWebInterface {
- @NonNull SliceStoreActivity mActivity;
+public class SlicePurchaseWebInterface {
+ @NonNull SlicePurchaseActivity mActivity;
- public SliceStoreWebInterface(@NonNull SliceStoreActivity activity) {
+ public SlicePurchaseWebInterface(@NonNull SlicePurchaseActivity activity) {
mActivity = activity;
}
+
/**
* Interface method allowing the carrier website to get the premium capability
* that was requested to purchase.
@@ -40,7 +41,7 @@
* This can be called using the JavaScript below:
* <script type="text/javascript">
* function getRequestedCapability(duration) {
- * SliceStoreWebInterface.getRequestedCapability();
+ * SlicePurchaseWebInterface.getRequestedCapability();
* }
* </script>
*/
@@ -50,13 +51,14 @@
}
/**
- * Interface method allowing the carrier website to notify the SliceStore of a successful
- * premium capability purchase and the duration for which the premium capability is purchased.
+ * Interface method allowing the carrier website to notify the slice purchase application of
+ * a successful premium capability purchase and the duration for which the premium capability is
+ * purchased.
*
* This can be called using the JavaScript below:
* <script type="text/javascript">
* function notifyPurchaseSuccessful(duration_ms_long = 0) {
- * SliceStoreWebInterface.notifyPurchaseSuccessful(duration_ms_long);
+ * SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long);
* }
* </script>
*
@@ -68,22 +70,23 @@
}
/**
- * Interface method allowing the carrier website to notify the SliceStore of a failed
- * premium capability purchase.
+ * Interface method allowing the carrier website to notify the slice purchase application of
+ * a failed premium capability purchase.
*
* This can be called using the JavaScript below:
* <script type="text/javascript">
- * function notifyPurchaseFailed() {
- * SliceStoreWebInterface.notifyPurchaseFailed();
+ * function notifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") {
+ * SlicePurchaseWebInterface.notifyPurchaseFailed();
* }
* </script>
*
* @param failureCode The failure code.
- * @param failureReason If the failure code is {@link SliceStore#FAILURE_CODE_UNKNOWN},
+ * @param failureReason If the failure code is
+ * {@link SlicePurchaseController#FAILURE_CODE_UNKNOWN},
* the human-readable reason for failure.
*/
@JavascriptInterface
- public void notifyPurchaseFailed(@SliceStore.FailureCode int failureCode,
+ public void notifyPurchaseFailed(@SlicePurchaseController.FailureCode int failureCode,
@Nullable String failureReason) {
mActivity.onPurchaseFailed(failureCode, failureReason);
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
deleted file mode 100644
index 348e389..0000000
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SliceStoreActivity.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-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.WebView;
-
-import com.android.phone.slicestore.SliceStore;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Activity that launches when the user clicks on the network boost notification.
- * This will open a {@link WebView} for the carrier website to allow the user to complete the
- * premium capability purchase.
- * The carrier website can get the requested premium capability using the JavaScript interface
- * method {@code SliceStoreWebInterface.getRequestedCapability()}.
- * If the purchase is successful, the carrier website shall notify SliceStore using the JavaScript
- * interface method {@code SliceStoreWebInterface.notifyPurchaseSuccessful(duration)}, where
- * {@code duration} is the duration of the network boost.
- * If the purchase was not successful, the carrier website shall notify SliceStore using the
- * JavaScript interface method {@code SliceStoreWebInterface.notifyPurchaseFailed()}.
- * If either of these notification methods are not called, the purchase cannot be completed
- * successfully and the purchase request will eventually time out.
- */
-public class SliceStoreActivity extends Activity {
- private static final String TAG = "SliceStoreActivity";
-
- private @NonNull WebView mWebView;
- private @NonNull Context mApplicationContext;
- private int mSubId;
- @TelephonyManager.PremiumCapability protected int mCapability;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Intent intent = getIntent();
- mSubId = intent.getIntExtra(SliceStore.EXTRA_SUB_ID,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mCapability = intent.getIntExtra(SliceStore.EXTRA_PREMIUM_CAPABILITY,
- SliceStore.PREMIUM_CAPABILITY_INVALID);
- mApplicationContext = getApplicationContext();
- URL url = getUrl();
- logd("onCreate: subId=" + mSubId + ", capability="
- + TelephonyManager.convertPremiumCapabilityToString(mCapability)
- + ", url=" + url);
-
- // Cancel network boost notification
- mApplicationContext.getSystemService(NotificationManager.class)
- .cancel(SliceStoreBroadcastReceiver.NETWORK_BOOST_NOTIFICATION_TAG, mCapability);
-
- // Verify intent and values are valid
- if (!SliceStoreBroadcastReceiver.isIntentValid(intent)) {
- loge("Not starting SliceStoreActivity with an invalid Intent: " + intent);
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- intent, SliceStore.EXTRA_INTENT_REQUEST_FAILED);
- finishAndRemoveTask();
- return;
- }
- if (url == null) {
- String error = "Unable to create a URL from carrier configs.";
- loge(error);
- Intent data = new Intent();
- data.putExtra(SliceStore.EXTRA_FAILURE_CODE,
- SliceStore.FAILURE_CODE_CARRIER_URL_UNAVAILABLE);
- data.putExtra(SliceStore.EXTRA_FAILURE_REASON, error);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_CARRIER_ERROR, data);
- finishAndRemoveTask();
- return;
- }
- if (mSubId != SubscriptionManager.getDefaultSubscriptionId()) {
- loge("Unable to start SliceStore on the non-default data subscription: " + mSubId);
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- intent, SliceStore.EXTRA_INTENT_NOT_DEFAULT_DATA);
- finishAndRemoveTask();
- return;
- }
-
- // Create a reference to this activity in SliceStoreBroadcastReceiver
- SliceStoreBroadcastReceiver.updateSliceStoreActivity(mCapability, this);
-
- // Create and configure WebView
- mWebView = new WebView(this);
- // Enable JavaScript for the carrier purchase website to send results back to SliceStore
- mWebView.getSettings().setJavaScriptEnabled(true);
- mWebView.addJavascriptInterface(new SliceStoreWebInterface(this), "SliceStoreWebInterface");
-
- // Display WebView
- setContentView(mWebView);
- mWebView.loadUrl(url.toString());
- }
-
- protected void onPurchaseSuccessful(long duration) {
- logd("onPurchaseSuccessful: Carrier website indicated successfully purchased premium "
- + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability)
- + " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes.");
- Intent intent = new Intent();
- intent.putExtra(SliceStore.EXTRA_PURCHASE_DURATION, duration);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_SUCCESS, intent);
- finishAndRemoveTask();
- }
-
- protected void onPurchaseFailed(@SliceStore.FailureCode int failureCode,
- @Nullable String failureReason) {
- logd("onPurchaseFailed: Carrier website indicated purchase failed for premium capability "
- + TelephonyManager.convertPremiumCapabilityToString(mCapability) + " with code: "
- + SliceStore.convertFailureCodeToString(failureCode) + " and reason: "
- + failureReason);
- Intent data = new Intent();
- data.putExtra(SliceStore.EXTRA_FAILURE_CODE, failureCode);
- data.putExtra(SliceStore.EXTRA_FAILURE_REASON, failureReason);
- SliceStoreBroadcastReceiver.sendSliceStoreResponseWithData(
- mApplicationContext, getIntent(), SliceStore.EXTRA_INTENT_CARRIER_ERROR, data);
- finishAndRemoveTask();
- }
-
- @Override
- public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- // Pressing back in the WebView will go to the previous page instead of closing SliceStore.
- if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
- mWebView.goBack();
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- protected void onDestroy() {
- logd("onDestroy: User canceled the purchase by closing the application.");
- SliceStoreBroadcastReceiver.sendSliceStoreResponse(
- getIntent(), SliceStore.EXTRA_INTENT_CANCELED);
- SliceStoreBroadcastReceiver.removeSliceStoreActivity(mCapability);
- super.onDestroy();
- }
-
- @Nullable private URL getUrl() {
- String url = mApplicationContext.getSystemService(CarrierConfigManager.class)
- .getConfigForSubId(mSubId).getString(
- CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
- try {
- return new URL(url);
- } catch (MalformedURLException e) {
- loge("Invalid URL: " + url);
- }
- return null;
- }
-
- private static void logd(@NonNull String s) {
- Log.d(TAG, s);
- }
-
- private static void loge(@NonNull String s) {
- Log.e(TAG, s);
- }
-}