• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.phone.slice;
18 
19 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS;
20 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED;
21 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED;
22 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR;
23 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED;
24 import static android.telephony.TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION;
25 
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.PendingIntent;
30 import android.content.BroadcastReceiver;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.content.SharedPreferences;
36 import android.net.ConnectivityManager;
37 import android.os.AsyncResult;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.Looper;
41 import android.os.Message;
42 import android.os.PersistableBundle;
43 import android.os.UserHandle;
44 import android.provider.DeviceConfig;
45 import android.sysprop.TelephonyProperties;
46 import android.telephony.AnomalyReporter;
47 import android.telephony.CarrierConfigManager;
48 import android.telephony.SubscriptionManager;
49 import android.telephony.TelephonyManager;
50 import android.telephony.data.NetworkSliceInfo;
51 import android.telephony.data.NetworkSlicingConfig;
52 import android.telephony.data.RouteSelectionDescriptor;
53 import android.telephony.data.TrafficDescriptor;
54 import android.telephony.data.UrspRule;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.webkit.URLUtil;
58 import android.webkit.WebView;
59 
60 import com.android.internal.annotations.VisibleForTesting;
61 import com.android.internal.telephony.Phone;
62 import com.android.internal.telephony.flags.FeatureFlags;
63 
64 import java.lang.annotation.Retention;
65 import java.lang.annotation.RetentionPolicy;
66 import java.net.MalformedURLException;
67 import java.net.URISyntaxException;
68 import java.net.URL;
69 import java.time.LocalDate;
70 import java.time.ZoneId;
71 import java.time.format.DateTimeParseException;
72 import java.util.Arrays;
73 import java.util.HashMap;
74 import java.util.HashSet;
75 import java.util.Map;
76 import java.util.Set;
77 import java.util.UUID;
78 import java.util.concurrent.Executor;
79 import java.util.concurrent.TimeUnit;
80 import java.util.function.Consumer;
81 
82 /**
83  * The SlicePurchaseController controls the purchase and availability of all cellular premium
84  * capabilities. Applications can check whether premium capabilities are available by calling
85  * {@link TelephonyManager#isPremiumCapabilityAvailableForPurchase(int)}. If this returns true,
86  * they can then call {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
87  * to purchase the premium capability. If all conditions are met, a notification will be displayed
88  * to the user prompting them to purchase the premium capability. If the user confirms on the
89  * notification, a {@link WebView} will open that allows the user to purchase the premium capability
90  * from the carrier. If the purchase is successful, the premium capability will be available for
91  * all applications to request through {@link ConnectivityManager#requestNetwork}.
92  */
93 public class SlicePurchaseController extends Handler {
94     @NonNull private static final String TAG = "SlicePurchaseController";
95 
96     /** Unknown failure code. */
97     public static final int FAILURE_CODE_UNKNOWN = 0;
98     /** Performance boost purchase failed because the carrier URL is unavailable. */
99     public static final int FAILURE_CODE_CARRIER_URL_UNAVAILABLE = 1;
100     /** Performance boost purchase failed because user authentication failed. */
101     public static final int FAILURE_CODE_AUTHENTICATION_FAILED = 2;
102     /** Performance boost purchase failed because the payment failed. */
103     public static final int FAILURE_CODE_PAYMENT_FAILED = 3;
104     /**
105      * Performance boost purchase failed because the content type was specified but
106      * user data does not exist.
107      */
108     public static final int FAILURE_CODE_NO_USER_DATA = 4;
109 
110     /**
111      * Failure codes that the carrier website can return when a premium capability purchase fails.
112      */
113     @Retention(RetentionPolicy.SOURCE)
114     @IntDef(prefix = { "FAILURE_CODE_" }, value = {
115             FAILURE_CODE_UNKNOWN,
116             FAILURE_CODE_CARRIER_URL_UNAVAILABLE,
117             FAILURE_CODE_AUTHENTICATION_FAILED,
118             FAILURE_CODE_PAYMENT_FAILED,
119             FAILURE_CODE_NO_USER_DATA})
120     public @interface FailureCode {}
121 
122     /** Value for an invalid premium capability. */
123     public static final int PREMIUM_CAPABILITY_INVALID = -1;
124 
125     /** Asset URL for the slice_purchase_test.html file. */
126     public static final String SLICE_PURCHASE_TEST_FILE =
127             "file:///android_asset/slice_purchase_test.html";
128 
129     /** Purchasing the premium capability is no longer throttled. */
130     private static final int EVENT_PURCHASE_UNTHROTTLED = 1;
131     /** Slicing config changed. */
132     private static final int EVENT_SLICING_CONFIG_CHANGED = 2;
133     /** Start slice purchase application. */
134     private static final int EVENT_START_SLICE_PURCHASE_APP = 3;
135     /**
136      * Premium capability was not purchased within the timeout specified by
137      * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG}.
138      */
139     private static final int EVENT_PURCHASE_TIMEOUT = 4;
140     /**
141      * Network did not set up the slicing configuration within the timeout specified by
142      * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG}.
143      */
144     private static final int EVENT_SETUP_TIMEOUT = 5;
145     /** Device config changed. */
146     private static final int EVENT_DEVICE_CONFIG_CHANGED = 6;
147 
148     /** UUID to report an anomaly when a premium capability is throttled twice in a row. */
149     private static final String UUID_CAPABILITY_THROTTLED_TWICE =
150             "15574927-e2e2-4593-99d4-2f340d22b383";
151     /** UUID to report an anomaly when receiving an invalid phone ID. */
152     private static final String UUID_INVALID_PHONE_ID = "ced79f1a-8ac0-4260-8cf3-08b54c0494f3";
153     /** UUID to report an anomaly when receiving an unknown action. */
154     private static final String UUID_UNKNOWN_ACTION = "0197efb0-dab1-4b0a-abaf-ac9336ec7923";
155     /** UUID to report an anomaly when receiving an unknown failure code with a non-empty reason. */
156     private static final String UUID_UNKNOWN_FAILURE_CODE = "76943b23-4415-400c-9855-b534fc4fc62c";
157     /**
158      * UUID to report an anomaly when the network fails to set up a slicing configuration after
159      * the user purchases a premium capability.
160      */
161     private static final String UUID_NETWORK_SETUP_FAILED = "12eeffbf-08f8-40ed-9a00-d344199552fc";
162 
163     /**
164      * Action to start the slice purchase application and display the
165      * performance boost notification.
166      */
167     public static final String ACTION_START_SLICE_PURCHASE_APP =
168             "com.android.phone.slice.action.START_SLICE_PURCHASE_APP";
169     /** Action indicating the premium capability purchase was not completed in time. */
170     public static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT =
171             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_TIMEOUT";
172     /** Action indicating the performance boost notification or WebView was canceled. */
173     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED =
174             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CANCELED";
175     /** Action indicating a carrier error prevented premium capability purchase. */
176     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR =
177             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR";
178     /**
179      * Action indicating a Telephony or slice purchase application error prevented premium
180      * capability purchase.
181      */
182     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED =
183             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED";
184     /** Action indicating the purchase request was not made on the default data subscription. */
185     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION =
186             "com.android.phone.slice.action."
187                     + "SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION";
188     /**
189      * Action indicating the performance boost notification was not shown because the user
190      * disabled notifications for the application or channel.
191      */
192     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED =
193             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED";
194     /** Action indicating the purchase request was successful. */
195     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS =
196             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_SUCCESS";
197     /**
198      * Action indicating the slice purchase application showed the performance boost notification.
199      */
200     private static final String ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN =
201             "com.android.phone.slice.action.SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN";
202 
203     /** Extra for the phone index to send to the slice purchase application. */
204     public static final String EXTRA_PHONE_ID = "com.android.phone.slice.extra.PHONE_ID";
205     /** Extra for the subscription ID to send to the slice purchase application. */
206     public static final String EXTRA_SUB_ID = "com.android.phone.slice.extra.SUB_ID";
207     /**
208      * Extra for the requested premium capability to purchase from the slice purchase application.
209      */
210     public static final String EXTRA_PREMIUM_CAPABILITY =
211             "com.android.phone.slice.extra.PREMIUM_CAPABILITY";
212     /** Extra for the carrier URL to display to the user to allow premium capability purchase. */
213     public static final String EXTRA_PURCHASE_URL = "com.android.phone.slice.extra.PURCHASE_URL";
214     /** Extra for the duration of the purchased premium capability. */
215     public static final String EXTRA_PURCHASE_DURATION =
216             "com.android.phone.slice.extra.PURCHASE_DURATION";
217     /** Extra for the {@link FailureCode} why the premium capability purchase failed. */
218     public static final String EXTRA_FAILURE_CODE = "com.android.phone.slice.extra.FAILURE_CODE";
219     /** Extra for the human-readable reason why the premium capability purchase failed. */
220     public static final String EXTRA_FAILURE_REASON =
221             "com.android.phone.slice.extra.FAILURE_REASON";
222     /** Extra for the user's carrier. */
223     public static final String EXTRA_CARRIER = "com.android.phone.slice.extra.CARRIER";
224     /** Extra for the user data received from the entitlement service to send to the webapp. */
225     public static final String EXTRA_USER_DATA = "com.android.phone.slice.extra.USER_DATA";
226     /** Extra for the contents type received from the entitlement service to send to the webapp. */
227     public static final String EXTRA_CONTENTS_TYPE = "com.android.phone.slice.extra.CONTENTS_TYPE";
228     /**
229      * Extra for the canceled PendingIntent that the slice purchase application can send as a
230      * response if the performance boost notification or WebView was canceled by the user.
231      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED}.
232      */
233     public static final String EXTRA_INTENT_CANCELED =
234             "com.android.phone.slice.extra.INTENT_CANCELED";
235     /**
236      * Extra for the carrier error PendingIntent that the slice purchase application can send as a
237      * response if the premium capability purchase request failed due to a carrier error.
238      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR}.
239      * Sender can modify the intent to specify the failure code and reason for failure with
240      * {@link #EXTRA_FAILURE_CODE} and {@link #EXTRA_FAILURE_REASON}.
241      */
242     public static final String EXTRA_INTENT_CARRIER_ERROR =
243             "com.android.phone.slice.extra.INTENT_CARRIER_ERROR";
244     /**
245      * Extra for the request failed PendingIntent that the slice purchase application can send as a
246      * response if the premium capability purchase request failed due to an error in Telephony or
247      * the slice purchase application.
248      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED}.
249      */
250     public static final String EXTRA_INTENT_REQUEST_FAILED =
251             "com.android.phone.slice.extra.INTENT_REQUEST_FAILED";
252     /**
253      * Extra for the not-default data subscription ID PendingIntent that the slice purchase
254      * application can send as a response if the premium capability purchase request failed because
255      * it was not requested on the default data subscription.
256      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION}.
257      */
258     public static final String EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION =
259             "com.android.phone.slice.extra.INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION";
260     /**
261      * Extra for the notifications disabled PendingIntent that the slice purchase application can
262      * send as a response if the premium capability purchase request failed because the user
263      * disabled notifications for the application or channel.
264      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED}.
265      */
266     public static final String EXTRA_INTENT_NOTIFICATIONS_DISABLED =
267             "com.android.phone.slice.extra.INTENT_NOTIFICATIONS_DISABLED";
268     /**
269      * Extra for the success PendingIntent that the slice purchase application can send as a
270      * response if the premium capability purchase request was successful.
271      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS}.
272      * Sender can modify the intent to specify a purchase duration with
273      * {@link #EXTRA_PURCHASE_DURATION}.
274      */
275     public static final String EXTRA_INTENT_SUCCESS =
276             "com.android.phone.slice.extra.INTENT_SUCCESS";
277     /**
278      * Extra for the PendingIntent that the slice purchase application can send to indicate
279      * that it displayed the performance boost notification to the user.
280      * Sends {@link #ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN}.
281      */
282     public static final String EXTRA_INTENT_NOTIFICATION_SHOWN =
283             "com.android.phone.slice.extra.NOTIFICATION_SHOWN";
284 
285     /** Component name for the SlicePurchaseBroadcastReceiver. */
286     private static final ComponentName SLICE_PURCHASE_APP_COMPONENT_NAME =
287             ComponentName.unflattenFromString(
288                     "com.android.carrierdefaultapp/.SlicePurchaseBroadcastReceiver");
289 
290     /** Shared preference name for performance boost notification preferences. */
291     private static final String PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES =
292             "performance_boost_notification_preferences";
293     /** Shared preference key for daily count of performance boost notifications. */
294     private static final String KEY_DAILY_NOTIFICATION_COUNT = "daily_notification_count";
295     /** Shared preference key for monthly count of performance boost notifications. */
296     private static final String KEY_MONTHLY_NOTIFICATION_COUNT = "monthly_notification_count";
297     /** DeviceConfig key for whether the slicing upsell feature is enabled. */
298     private static final String KEY_ENABLE_SLICING_UPSELL = "enable_slicing_upsell";
299     /**
300      * Shared preference key for the date the daily or monthly counts of performance boost
301      * notifications were last reset.
302      * A String with ISO-8601 format {@code YYYY-MM-DD}, from {@link LocalDate#toString}.
303      * For example, if the count was last updated on December 25, 2020, this would be `2020-12-25`.
304      */
305     private static final String KEY_NOTIFICATION_COUNT_LAST_RESET_DATE =
306             "notification_count_last_reset_date";
307 
308     /** Map of phone ID -> SlicePurchaseController instances. */
309     @NonNull private static final Map<Integer, SlicePurchaseController> sInstances =
310             new HashMap<>();
311 
312     /** The Phone instance used to create the SlicePurchaseController. */
313     @NonNull private final Phone mPhone;
314     /** Feature flags to control behavior and errors. */
315     @NonNull private final FeatureFlags mFeatureFlags;
316     /** The set of capabilities that are pending network setup. */
317     @NonNull private final Set<Integer> mPendingSetupCapabilities = new HashSet<>();
318     /** The set of throttled capabilities. */
319     @NonNull private final Set<Integer> mThrottledCapabilities = new HashSet<>();
320     /** A map of pending capabilities to the onComplete message for the purchase request. */
321     @NonNull private final Map<Integer, Message> mPendingPurchaseCapabilities = new HashMap<>();
322     /**
323      * A map of capabilities to the SlicePurchaseControllerBroadcastReceiver to handle
324      * slice purchase application responses.
325      */
326     @NonNull private final Map<Integer, SlicePurchaseControllerBroadcastReceiver>
327             mSlicePurchaseControllerBroadcastReceivers = new HashMap<>();
328     /** The current network slicing configuration. */
329     @Nullable private NetworkSlicingConfig mSlicingConfig;
330 
331     /** LocalDate to use when resetting notification counts. {@code null} except when testing. */
332     @Nullable private LocalDate mLocalDate;
333     /** The number of times the performance boost notification has been shown today. */
334     private int mDailyCount;
335     /** The number of times the performance boost notification has been shown this month. */
336     private int mMonthlyCount;
337     /** {@code true} if the slicing upsell feature is enabled and {@code false} otherwise. */
338     private boolean mIsSlicingUpsellEnabled;
339 
340     /**
341      * BroadcastReceiver to receive responses from the slice purchase application.
342      */
343     private class SlicePurchaseControllerBroadcastReceiver extends BroadcastReceiver {
344         @TelephonyManager.PremiumCapability private final int mCapability;
345 
346         /**
347          * Create a SlicePurchaseControllerBroadcastReceiver for the given capability
348          *
349          * @param capability The requested premium capability to listen to response for.
350          */
SlicePurchaseControllerBroadcastReceiver( @elephonyManager.PremiumCapability int capability)351         SlicePurchaseControllerBroadcastReceiver(
352                 @TelephonyManager.PremiumCapability int capability) {
353             mCapability = capability;
354         }
355 
356         /**
357          * Process responses from the slice purchase application.
358          *
359          * @param context The Context in which the receiver is running.
360          * @param intent The Intent being received.
361          */
362         @Override
onReceive(@onNull Context context, @NonNull Intent intent)363         public void onReceive(@NonNull Context context, @NonNull Intent intent) {
364             String action = intent.getAction();
365             logd("SlicePurchaseControllerBroadcastReceiver("
366                     + TelephonyManager.convertPremiumCapabilityToString(mCapability)
367                     + ") received action: " + action);
368             int phoneId = intent.getIntExtra(EXTRA_PHONE_ID,
369                     SubscriptionManager.INVALID_PHONE_INDEX);
370             int capability = intent.getIntExtra(EXTRA_PREMIUM_CAPABILITY,
371                     PREMIUM_CAPABILITY_INVALID);
372             if (SlicePurchaseController.getInstance(phoneId) == null) {
373                 reportAnomaly(UUID_INVALID_PHONE_ID, "SlicePurchaseControllerBroadcastReceiver( "
374                         + TelephonyManager.convertPremiumCapabilityToString(mCapability)
375                         + ") received invalid phoneId: " + phoneId);
376                 return;
377             } else if (capability != mCapability) {
378                 logd("SlicePurchaseControllerBroadcastReceiver("
379                         + TelephonyManager.convertPremiumCapabilityToString(mCapability)
380                         + ") ignoring intent for capability "
381                         + TelephonyManager.convertPremiumCapabilityToString(capability));
382                 return;
383             }
384             switch (action) {
385                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED: {
386                     logd("Slice purchase application canceled for capability: "
387                             + TelephonyManager.convertPremiumCapabilityToString(capability));
388                     SlicePurchaseController.getInstance(phoneId)
389                             .handlePurchaseResult(capability,
390                             TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED,
391                             true);
392                     break;
393                 }
394                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR: {
395                     int failureCode = intent.getIntExtra(EXTRA_FAILURE_CODE, FAILURE_CODE_UNKNOWN);
396                     String failureReason = intent.getStringExtra(EXTRA_FAILURE_REASON);
397                     SlicePurchaseController.getInstance(phoneId).onCarrierError(
398                             capability, failureCode, failureReason);
399                     break;
400                 }
401                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED: {
402                     logd("Purchase premium capability request failed for capability: "
403                             + TelephonyManager.convertPremiumCapabilityToString(capability));
404                     SlicePurchaseController.getInstance(phoneId)
405                             .handlePurchaseResult(capability,
406                             TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED,
407                             false);
408                     break;
409                 }
410                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION: {
411                     logd("Purchase premium capability request was not made on the default data "
412                             + "subscription for capability: "
413                             + TelephonyManager.convertPremiumCapabilityToString(capability));
414                     SlicePurchaseController.getInstance(phoneId)
415                             .handlePurchaseResult(capability,
416                             PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
417                             false);
418                     break;
419                 }
420                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED: {
421                     logd("Slice purchase application unable to show notification for capability: "
422                             + TelephonyManager.convertPremiumCapabilityToString(capability)
423                             + " because the user has disabled notifications.");
424                     int error = mFeatureFlags.slicingAdditionalErrorCodes()
425                             ? TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED
426                             : TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED;
427                     SlicePurchaseController.getInstance(phoneId)
428                             .handlePurchaseResult(capability, error, true);
429                     break;
430                 }
431                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS: {
432                     long duration = intent.getLongExtra(EXTRA_PURCHASE_DURATION, 0);
433                     SlicePurchaseController.getInstance(phoneId).onCarrierSuccess(
434                             capability, duration);
435                     break;
436                 }
437                 case ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN: {
438                     SlicePurchaseController.getInstance(phoneId).onNotificationShown();
439                     break;
440                 }
441                 default:
442                     reportAnomaly(UUID_UNKNOWN_ACTION, "SlicePurchaseControllerBroadcastReceiver("
443                             + TelephonyManager.convertPremiumCapabilityToString(mCapability)
444                             + ") received unknown action: " + action);
445                     break;
446             }
447         }
448     }
449 
450     /**
451      * Get the static SlicePurchaseController instance for the given phone or create one if it
452      * doesn't exist.
453      *
454      * @param phone The Phone to get the SlicePurchaseController for.
455      * @return The static SlicePurchaseController instance.
456      */
getInstance(@onNull Phone phone, @NonNull FeatureFlags featureFlags)457     @NonNull public static synchronized SlicePurchaseController getInstance(@NonNull Phone phone,
458             @NonNull FeatureFlags featureFlags) {
459         // TODO: Add listeners for multi sim setting changed (maybe carrier config changed too)
460         //  that dismiss notifications and update SlicePurchaseController instance
461         int phoneId = phone.getPhoneId();
462         if (sInstances.get(phoneId) == null) {
463             HandlerThread handlerThread = new HandlerThread("SlicePurchaseController");
464             handlerThread.start();
465             sInstances.put(phoneId,
466                     new SlicePurchaseController(phone, featureFlags, handlerThread.getLooper()));
467         }
468         return sInstances.get(phoneId);
469     }
470 
471     /**
472      * Get the static SlicePurchaseController instance for the given phone ID if it exists.
473      *
474      * @param phoneId The phone ID to get the SlicePurchaseController for.
475      * @return The static SlicePurchaseController instance or
476      *         {@code null} if it hasn't been created yet.
477      */
getInstance(int phoneId)478     @Nullable private static SlicePurchaseController getInstance(int phoneId) {
479         return sInstances.get(phoneId);
480     }
481 
482     /**
483      * Create a SlicePurchaseController for the given phone on the given looper.
484      *
485      * @param phone The Phone to create the SlicePurchaseController for.
486      * @param featureFlags The FeatureFlags that are supported.
487      * @param looper The Looper to run the SlicePurchaseController on.
488      */
489     @VisibleForTesting
SlicePurchaseController(@onNull Phone phone, @NonNull FeatureFlags featureFlags, @NonNull Looper looper)490     public SlicePurchaseController(@NonNull Phone phone, @NonNull FeatureFlags featureFlags,
491             @NonNull Looper looper) {
492         super(looper);
493         mPhone = phone;
494         mFeatureFlags = featureFlags;
495         // TODO: Create a cached value for slicing config in DataIndication and initialize here
496         mPhone.mCi.registerForSlicingConfigChanged(this, EVENT_SLICING_CONFIG_CHANGED, null);
497         mIsSlicingUpsellEnabled = DeviceConfig.getBoolean(
498                 DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_SLICING_UPSELL, false);
499         DeviceConfig.addOnPropertiesChangedListener(
500                 DeviceConfig.NAMESPACE_TELEPHONY, Runnable::run,
501                 properties -> {
502                     if (TextUtils.equals(DeviceConfig.NAMESPACE_TELEPHONY,
503                             properties.getNamespace())) {
504                         sendEmptyMessage(EVENT_DEVICE_CONFIG_CHANGED);
505                     }
506                 });
507         updateNotificationCounts();
508     }
509 
510     /**
511      * Set the LocalDate to use for resetting daily and monthly notification counts.
512      *
513      * @param localDate The LocalDate instance to use.
514      */
515     @VisibleForTesting
setLocalDate(@onNull LocalDate localDate)516     public void setLocalDate(@NonNull LocalDate localDate) {
517         mLocalDate = localDate;
518     }
519 
520     /**
521      * Set the NetworkSlicingConfig to use for determining whether the premium capability was
522      * successfully set up on the carrier network.
523      *
524      * @param slicingConfig The LocalDate instance to use.
525      */
526     @VisibleForTesting
setSlicingConfig(@onNull NetworkSlicingConfig slicingConfig)527     public void setSlicingConfig(@NonNull NetworkSlicingConfig slicingConfig) {
528         mSlicingConfig = slicingConfig;
529     }
530 
531     @Override
handleMessage(@onNull Message msg)532     public void handleMessage(@NonNull Message msg) {
533         switch (msg.what) {
534             case EVENT_PURCHASE_UNTHROTTLED: {
535                 int capability = (int) msg.obj;
536                 logd("EVENT_PURCHASE_UNTHROTTLED: for capability "
537                         + TelephonyManager.convertPremiumCapabilityToString(capability));
538                 mThrottledCapabilities.remove(capability);
539                 break;
540             }
541             case EVENT_SLICING_CONFIG_CHANGED: {
542                 AsyncResult ar = (AsyncResult) msg.obj;
543                 NetworkSlicingConfig config = (NetworkSlicingConfig) ar.result;
544                 logd("EVENT_SLICING_CONFIG_CHANGED: previous= " + mSlicingConfig);
545                 logd("EVENT_SLICING_CONFIG_CHANGED: current= " + config);
546                 mSlicingConfig = config;
547                 onSlicingConfigChanged();
548                 break;
549             }
550             case EVENT_START_SLICE_PURCHASE_APP: {
551                 int capability = (int) msg.obj;
552                 logd("EVENT_START_SLICE_PURCHASE_APP: "
553                         + TelephonyManager.convertPremiumCapabilityToString(capability));
554                 onStartSlicePurchaseApplication(capability);
555                 break;
556             }
557             case EVENT_PURCHASE_TIMEOUT: {
558                 int capability = (int) msg.obj;
559                 logd("EVENT_PURCHASE_TIMEOUT: for capability "
560                         + TelephonyManager.convertPremiumCapabilityToString(capability));
561                 onTimeout(capability);
562                 break;
563             }
564             case EVENT_SETUP_TIMEOUT:
565                 int capability = (int) msg.obj;
566                 logd("EVENT_SETUP_TIMEOUT: for capability "
567                         + TelephonyManager.convertPremiumCapabilityToString(capability));
568                 onSetupTimeout(capability);
569                 break;
570             case EVENT_DEVICE_CONFIG_CHANGED:
571                 boolean isSlicingUpsellEnabled = DeviceConfig.getBoolean(
572                         DeviceConfig.NAMESPACE_TELEPHONY, KEY_ENABLE_SLICING_UPSELL, false);
573                 if (isSlicingUpsellEnabled != mIsSlicingUpsellEnabled) {
574                     logd("EVENT_DEVICE_CONFIG_CHANGED: from " + mIsSlicingUpsellEnabled + " to "
575                             + isSlicingUpsellEnabled);
576                     mIsSlicingUpsellEnabled = isSlicingUpsellEnabled;
577                 }
578                 break;
579             default:
580                 loge("Unknown event: " + msg.obj);
581         }
582     }
583 
584     /**
585      * Check whether the given premium capability is available for purchase from the carrier.
586      *
587      * @param capability The premium capability to check.
588      * @return Whether the given premium capability is available to purchase.
589      */
isPremiumCapabilityAvailableForPurchase( @elephonyManager.PremiumCapability int capability)590     public boolean isPremiumCapabilityAvailableForPurchase(
591             @TelephonyManager.PremiumCapability int capability) {
592         if (!arePremiumCapabilitiesSupportedByDevice()) {
593             logd("Premium capabilities unsupported by the device.");
594             return false;
595         }
596         if (!isPremiumCapabilitySupportedByCarrier(capability)) {
597             logd("Premium capability "
598                     + TelephonyManager.convertPremiumCapabilityToString(capability)
599                     + " unsupported by the carrier.");
600             return false;
601         }
602         if (!isDefaultDataSub()) {
603             logd("Premium capability "
604                     + TelephonyManager.convertPremiumCapabilityToString(capability)
605                     + " unavailable on the non-default data subscription.");
606             return false;
607         }
608         logd("Premium capability "
609                 + TelephonyManager.convertPremiumCapabilityToString(capability)
610                 + " is available for purchase.");
611         return true;
612     }
613 
614     /**
615      * Purchase the given premium capability from the carrier.
616      *
617      * @param capability The premium capability to purchase.
618      * @param onComplete The callback message to send when the purchase request is complete.
619      */
purchasePremiumCapability( @elephonyManager.PremiumCapability int capability, @NonNull Message onComplete)620     public synchronized void purchasePremiumCapability(
621             @TelephonyManager.PremiumCapability int capability, @NonNull Message onComplete) {
622         logd("purchasePremiumCapability: "
623                 + TelephonyManager.convertPremiumCapabilityToString(capability));
624         // Check whether the premium capability can be purchased.
625         if (!arePremiumCapabilitiesSupportedByDevice()) {
626             sendPurchaseResult(capability,
627                     TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
628                     onComplete);
629             return;
630         }
631         if (!isPremiumCapabilitySupportedByCarrier(capability)) {
632             sendPurchaseResult(capability,
633                     PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
634                     onComplete);
635             return;
636         }
637         if (!isDefaultDataSub()) {
638             sendPurchaseResult(capability,
639                     PURCHASE_PREMIUM_CAPABILITY_RESULT_NOT_DEFAULT_DATA_SUBSCRIPTION,
640                     onComplete);
641             return;
642         }
643         if (isSlicingConfigActive(capability)) {
644             sendPurchaseResult(capability,
645                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
646                     onComplete);
647             return;
648         }
649         if (mPendingSetupCapabilities.contains(capability)) {
650             sendPurchaseResult(capability,
651                     TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_PENDING_NETWORK_SETUP,
652                     onComplete);
653             return;
654         }
655         if (mThrottledCapabilities.contains(capability)) {
656             sendPurchaseResult(capability,
657                     TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED,
658                     onComplete);
659             return;
660         }
661         if (!isNetworkAvailable()) {
662             sendPurchaseResult(capability,
663                     TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
664                     onComplete);
665             return;
666         }
667 
668         if (mPendingPurchaseCapabilities.containsKey(capability)) {
669             sendPurchaseResult(capability,
670                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
671                     onComplete);
672             return;
673         }
674 
675         // All state checks passed. Mark purchase pending and start the slice purchase application.
676         // Process through the handler since this method is synchronized.
677         mPendingPurchaseCapabilities.put(capability, onComplete);
678         sendMessage(obtainMessage(EVENT_START_SLICE_PURCHASE_APP, capability));
679     }
680 
sendPurchaseResult(@elephonyManager.PremiumCapability int capability, @TelephonyManager.PurchasePremiumCapabilityResult int result, @NonNull Message onComplete)681     private void sendPurchaseResult(@TelephonyManager.PremiumCapability int capability,
682             @TelephonyManager.PurchasePremiumCapabilityResult int result,
683             @NonNull Message onComplete) {
684         // Send the onComplete message with the purchase result.
685         logd("Purchase result for capability "
686                 + TelephonyManager.convertPremiumCapabilityToString(capability)
687                 + ": " + TelephonyManager.convertPurchaseResultToString(result));
688         AsyncResult.forMessage(onComplete, result, null);
689         onComplete.sendToTarget();
690     }
691 
handlePurchaseResult( @elephonyManager.PremiumCapability int capability, @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle)692     private void handlePurchaseResult(
693             @TelephonyManager.PremiumCapability int capability,
694             @TelephonyManager.PurchasePremiumCapabilityResult int result, boolean throttle) {
695         SlicePurchaseControllerBroadcastReceiver receiver =
696                 mSlicePurchaseControllerBroadcastReceivers.remove(capability);
697         if (receiver != null) {
698             mPhone.getContext().unregisterReceiver(receiver);
699         }
700         removeMessages(EVENT_PURCHASE_TIMEOUT, capability);
701         if (throttle) {
702             throttleCapability(capability, getThrottleDuration(result));
703         }
704         sendPurchaseResult(capability, result, mPendingPurchaseCapabilities.remove(capability));
705     }
706 
throttleCapability(@elephonyManager.PremiumCapability int capability, long throttleDuration)707     private void throttleCapability(@TelephonyManager.PremiumCapability int capability,
708             long throttleDuration) {
709         // Throttle subsequent requests if necessary.
710         if (!mThrottledCapabilities.contains(capability)) {
711             if (throttleDuration > 0) {
712                 logd("Throttle purchase requests for capability "
713                         + TelephonyManager.convertPremiumCapabilityToString(capability) + " for "
714                         + TimeUnit.MILLISECONDS.toMinutes(throttleDuration) + " minutes.");
715                 mThrottledCapabilities.add(capability);
716                 sendMessageDelayed(obtainMessage(EVENT_PURCHASE_UNTHROTTLED, capability),
717                         throttleDuration);
718             }
719         } else {
720             reportAnomaly(UUID_CAPABILITY_THROTTLED_TWICE,
721                     TelephonyManager.convertPremiumCapabilityToString(capability)
722                             + " is already throttled.");
723         }
724     }
725 
onSlicingConfigChanged()726     private void onSlicingConfigChanged() {
727         for (int capability : new int[] {TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY}) {
728             if (isSlicingConfigActive(capability) && hasMessages(EVENT_SETUP_TIMEOUT, capability)) {
729                 logd("Successfully set up slicing configuration for "
730                         + TelephonyManager.convertPremiumCapabilityToString(capability));
731                 mPendingSetupCapabilities.remove(capability);
732                 removeMessages(EVENT_SETUP_TIMEOUT, capability);
733             }
734         }
735     }
736 
737     /**
738      * @return A new PremiumNetworkEntitlementApi object.
739      */
740     @VisibleForTesting
getPremiumNetworkEntitlementApi()741     public PremiumNetworkEntitlementApi getPremiumNetworkEntitlementApi() {
742         return new PremiumNetworkEntitlementApi(mPhone, getCarrierConfigs());
743     }
744 
onStartSlicePurchaseApplication( @elephonyManager.PremiumCapability int capability)745     private void onStartSlicePurchaseApplication(
746             @TelephonyManager.PremiumCapability int capability) {
747         updateNotificationCounts();
748         if (mMonthlyCount >= getCarrierConfigs().getInt(
749                 CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_MONTHLY_NOTIFICATION_COUNT_INT)
750                 || mDailyCount >= getCarrierConfigs().getInt(
751                 CarrierConfigManager.KEY_PREMIUM_CAPABILITY_MAXIMUM_DAILY_NOTIFICATION_COUNT_INT)) {
752             logd("Reached maximum number of performance boost notifications.");
753             handlePurchaseResult(capability,
754                     TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED, false);
755             return;
756         }
757 
758         final PremiumNetworkEntitlementApi premiumNetworkEntitlementApi =
759                 getPremiumNetworkEntitlementApi();
760         PremiumNetworkEntitlementResponse premiumNetworkEntitlementResponse =
761                 premiumNetworkEntitlementApi.checkEntitlementStatus(capability);
762 
763         // invalid response for entitlement check
764         if (premiumNetworkEntitlementResponse == null
765                 || premiumNetworkEntitlementResponse.isInvalidResponse()) {
766             loge("Invalid response for entitlement check: " + premiumNetworkEntitlementResponse);
767             handlePurchaseResult(capability,
768                     PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, true);
769             return;
770         }
771 
772         if (!premiumNetworkEntitlementResponse.isPremiumNetworkCapabilityAllowed()) {
773             loge("Entitlement Check: Not allowed.");
774             handlePurchaseResult(capability,
775                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED, true);
776             return;
777         }
778 
779         if (premiumNetworkEntitlementResponse.isAlreadyPurchased()) {
780             logd("Entitlement Check: Already purchased/provisioned.");
781             handlePurchaseResult(capability,
782                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED, true);
783             return;
784         }
785 
786         if (premiumNetworkEntitlementResponse.isProvisioningInProgress()) {
787             logd("Entitlement Check: In progress.");
788             handlePurchaseResult(capability,
789                     PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS, true);
790             return;
791         }
792 
793         String purchaseUrl = getPurchaseUrl(premiumNetworkEntitlementResponse);
794         String carrier = getSimOperator();
795         if (TextUtils.isEmpty(purchaseUrl) || TextUtils.isEmpty(carrier)) {
796             handlePurchaseResult(capability,
797                     PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED, false);
798             return;
799         }
800 
801         // Start timeout for purchase completion.
802         long timeout = getCarrierConfigs().getLong(CarrierConfigManager
803                 .KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG);
804         logd("Start purchase timeout for "
805                 + TelephonyManager.convertPremiumCapabilityToString(capability) + " for "
806                 + TimeUnit.MILLISECONDS.toMinutes(timeout) + " minutes.");
807         sendMessageDelayed(obtainMessage(EVENT_PURCHASE_TIMEOUT, capability), timeout);
808 
809         // Broadcast start intent to start the slice purchase application
810         Intent intent = new Intent(ACTION_START_SLICE_PURCHASE_APP);
811         intent.setComponent(SLICE_PURCHASE_APP_COMPONENT_NAME);
812         intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
813         intent.putExtra(EXTRA_SUB_ID, mPhone.getSubId());
814         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
815         intent.putExtra(EXTRA_PURCHASE_URL, purchaseUrl);
816         intent.putExtra(EXTRA_CARRIER, carrier);
817         intent.putExtra(EXTRA_USER_DATA, premiumNetworkEntitlementResponse.mServiceFlowUserData);
818         intent.putExtra(EXTRA_CONTENTS_TYPE,
819                 premiumNetworkEntitlementResponse.mServiceFlowContentsType);
820         intent.putExtra(EXTRA_INTENT_CANCELED, createPendingIntent(
821                 ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED, capability, false));
822         intent.putExtra(EXTRA_INTENT_CARRIER_ERROR, createPendingIntent(
823                 ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR, capability, true));
824         intent.putExtra(EXTRA_INTENT_REQUEST_FAILED, createPendingIntent(
825                 ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED, capability, false));
826         intent.putExtra(EXTRA_INTENT_NOT_DEFAULT_DATA_SUBSCRIPTION, createPendingIntent(
827                 ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION, capability,
828                 false));
829         intent.putExtra(EXTRA_INTENT_NOTIFICATIONS_DISABLED, createPendingIntent(
830                 ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED, capability, false));
831         intent.putExtra(EXTRA_INTENT_SUCCESS, createPendingIntent(
832                 ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS, capability, true));
833         intent.putExtra(EXTRA_INTENT_NOTIFICATION_SHOWN, createPendingIntent(
834                 ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN, capability, false));
835         logd("Broadcasting start intent to SlicePurchaseBroadcastReceiver.");
836         if (mFeatureFlags.hsumBroadcast()) {
837             mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
838         } else {
839             mPhone.getContext().sendBroadcast(intent);
840         }
841 
842         // Listen for responses from the slice purchase application
843         mSlicePurchaseControllerBroadcastReceivers.put(capability,
844                 new SlicePurchaseControllerBroadcastReceiver(capability));
845         IntentFilter filter = new IntentFilter();
846         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CANCELED);
847         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_CARRIER_ERROR);
848         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_REQUEST_FAILED);
849         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOT_DEFAULT_DATA_SUBSCRIPTION);
850         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATIONS_DISABLED);
851         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_SUCCESS);
852         filter.addAction(ACTION_SLICE_PURCHASE_APP_RESPONSE_NOTIFICATION_SHOWN);
853         mPhone.getContext().registerReceiver(
854                 mSlicePurchaseControllerBroadcastReceivers.get(capability), filter,
855                 Context.RECEIVER_NOT_EXPORTED);
856     }
857 
858     /**
859      * Get a valid purchase URL from either entitlement response or carrier configs, if one exists.
860      *
861      * @param entitlementResponse The entitlement response to get the purchase URL from.
862      * @return A valid purchase URL or an empty string if one doesn't exist.
863      */
864     @VisibleForTesting
getPurchaseUrl( @onNull PremiumNetworkEntitlementResponse entitlementResponse)865     @NonNull public String getPurchaseUrl(
866             @NonNull PremiumNetworkEntitlementResponse entitlementResponse) {
867         String purchaseUrl = entitlementResponse.mServiceFlowURL;
868         if (!isUrlValid(purchaseUrl)) {
869             purchaseUrl = getCarrierConfigs().getString(
870                     CarrierConfigManager.KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING);
871             if (!isUrlValid(purchaseUrl)) {
872                 purchaseUrl = "";
873             }
874         }
875         return purchaseUrl;
876     }
877 
878     /**
879      * Get the SIM operator. This is the carrier name from the SIM rather than from the network,
880      * which will be the same regardless of whether the user is roaming or not.
881      *
882      * @return The operator name from the SIM.
883      */
884     @VisibleForTesting
getSimOperator()885     @Nullable public String getSimOperator() {
886         if (mPhone.getPhoneId() < TelephonyProperties.icc_operator_alpha().size()) {
887             return TelephonyProperties.icc_operator_alpha().get(mPhone.getPhoneId());
888         }
889         return null;
890     }
891 
892     /**
893      * Create the PendingIntent to allow the slice purchase application to send back responses.
894      *
895      * @param action The action that will be sent for this PendingIntent
896      * @param capability The premium capability that was requested.
897      * @param mutable {@code true} if the PendingIntent should be mutable and
898      *                {@code false} if it should be immutable.
899      * @return The PendingIntent for the given action and capability.
900      */
901     @VisibleForTesting
createPendingIntent(@onNull String action, @TelephonyManager.PremiumCapability int capability, boolean mutable)902     @NonNull public PendingIntent createPendingIntent(@NonNull String action,
903             @TelephonyManager.PremiumCapability int capability, boolean mutable) {
904         Intent intent = new Intent(action);
905         intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
906         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
907         intent.setPackage(mPhone.getContext().getPackageName());
908         return PendingIntent.getBroadcast(mPhone.getContext(), capability, intent,
909                 PendingIntent.FLAG_CANCEL_CURRENT
910                         | (mutable ? PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_IMMUTABLE));
911     }
912 
onTimeout(@elephonyManager.PremiumCapability int capability)913     private void onTimeout(@TelephonyManager.PremiumCapability int capability) {
914         logd("onTimeout: " + TelephonyManager.convertPremiumCapabilityToString(capability));
915         // Broadcast timeout intent to clean up the slice purchase notification and activity
916         Intent intent = new Intent(ACTION_SLICE_PURCHASE_APP_RESPONSE_TIMEOUT);
917         intent.setComponent(SLICE_PURCHASE_APP_COMPONENT_NAME);
918         intent.putExtra(EXTRA_PHONE_ID, mPhone.getPhoneId());
919         intent.putExtra(EXTRA_PREMIUM_CAPABILITY, capability);
920         logd("Broadcasting timeout intent to SlicePurchaseBroadcastReceiver.");
921         if (mFeatureFlags.hsumBroadcast()) {
922             mPhone.getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
923         } else {
924             mPhone.getContext().sendBroadcast(intent);
925         }
926 
927         handlePurchaseResult(
928                 capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT, true);
929     }
930 
onCarrierError(@elephonyManager.PremiumCapability int capability, @FailureCode int failureCode, @Nullable String failureReason)931     private void onCarrierError(@TelephonyManager.PremiumCapability int capability,
932             @FailureCode int failureCode, @Nullable String failureReason) {
933         logd("Carrier error for capability: "
934                 + TelephonyManager.convertPremiumCapabilityToString(capability) + " with code: "
935                 + convertFailureCodeToString(failureCode) + " and reason: " + failureReason);
936         if (failureCode == FAILURE_CODE_UNKNOWN && !TextUtils.isEmpty(failureReason)) {
937             reportAnomaly(UUID_UNKNOWN_FAILURE_CODE,
938                     "Failure code needs to be added for: " + failureReason);
939         }
940         handlePurchaseResult(capability,
941                 TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR, true);
942     }
943 
onCarrierSuccess(@elephonyManager.PremiumCapability int capability, long duration)944     private void onCarrierSuccess(@TelephonyManager.PremiumCapability int capability,
945             long duration) {
946         logd("Successfully purchased premium capability "
947                 + TelephonyManager.convertPremiumCapabilityToString(capability) + (duration > 0
948                 ? " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes." : "."));
949         mPendingSetupCapabilities.add(capability);
950         long setupDuration = getCarrierConfigs().getLong(
951                 CarrierConfigManager.KEY_PREMIUM_CAPABILITY_NETWORK_SETUP_TIME_MILLIS_LONG);
952         logd("Waiting " + TimeUnit.MILLISECONDS.toMinutes(setupDuration) + " minutes for the "
953                 + "network to set up the slicing configuration.");
954         sendMessageDelayed(obtainMessage(EVENT_SETUP_TIMEOUT, capability), setupDuration);
955         handlePurchaseResult(
956                 capability, TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS, false);
957     }
958 
onSetupTimeout(@elephonyManager.PremiumCapability int capability)959     private void onSetupTimeout(@TelephonyManager.PremiumCapability int capability) {
960         logd("onSetupTimeout: " + TelephonyManager.convertPremiumCapabilityToString(capability));
961         mPendingSetupCapabilities.remove(capability);
962         if (!isSlicingConfigActive(capability)) {
963             reportAnomaly(UUID_NETWORK_SETUP_FAILED,
964                     "Failed to set up slicing configuration for capability "
965                             + TelephonyManager.convertPremiumCapabilityToString(capability)
966                             + " within the time specified.");
967         }
968     }
969 
onNotificationShown()970     private void onNotificationShown() {
971         SharedPreferences sp = mPhone.getContext().getSharedPreferences(
972                 PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES, 0);
973         mDailyCount = sp.getInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0) + 1;
974         mMonthlyCount = sp.getInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0) + 1;
975         logd("Performance boost notification was shown " + mDailyCount + " times today and "
976                 + mMonthlyCount + " times this month.");
977 
978         SharedPreferences.Editor editor = sp.edit();
979         editor.putInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mDailyCount);
980         editor.putInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mMonthlyCount);
981         editor.apply();
982 
983         // Don't call updateNotificationCounts here because it will be called whenever a new
984         // purchase request comes in or when SlicePurchaseController is initialized.
985     }
986 
987     /**
988      * Update the current daily and monthly performance boost notification counts.
989      * If it has been at least a day since the last daily reset or at least a month since the last
990      * monthly reset, reset the current daily or monthly notification counts.
991      */
992     @VisibleForTesting
updateNotificationCounts()993     public void updateNotificationCounts() {
994         SharedPreferences sp = mPhone.getContext().getSharedPreferences(
995                 PERFORMANCE_BOOST_NOTIFICATION_PREFERENCES, 0);
996         mDailyCount = sp.getInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0);
997         mMonthlyCount = sp.getInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()), 0);
998 
999         if (mLocalDate == null) {
1000             // Standardize to UTC to prevent default time zone dependency
1001             mLocalDate = LocalDate.now(ZoneId.of("UTC"));
1002         }
1003         LocalDate lastLocalDate = LocalDate.of(1, 1, 1);
1004         String lastLocalDateString = sp.getString(
1005                 (KEY_NOTIFICATION_COUNT_LAST_RESET_DATE + mPhone.getPhoneId()), "");
1006         if (!TextUtils.isEmpty(lastLocalDateString)) {
1007             try {
1008                 lastLocalDate = LocalDate.parse(lastLocalDateString);
1009             } catch (DateTimeParseException e) {
1010                 loge("Error parsing LocalDate from SharedPreferences: " + e);
1011             }
1012         }
1013         logd("updateNotificationCounts: mDailyCount=" + mDailyCount + ", mMonthlyCount="
1014                 + mMonthlyCount + ", mLocalDate=" + mLocalDate + ", lastLocalDate="
1015                 + lastLocalDate);
1016 
1017         boolean resetMonthly = lastLocalDate.getYear() != mLocalDate.getYear()
1018                 || lastLocalDate.getMonthValue() != mLocalDate.getMonthValue();
1019         boolean resetDaily = resetMonthly
1020                 || lastLocalDate.getDayOfMonth() != mLocalDate.getDayOfMonth();
1021         if (resetDaily) {
1022             logd("Resetting daily" + (resetMonthly ? " and monthly" : "") + " notification count.");
1023             SharedPreferences.Editor editor = sp.edit();
1024             if (resetMonthly) {
1025                 mMonthlyCount = 0;
1026                 editor.putInt((KEY_MONTHLY_NOTIFICATION_COUNT + mPhone.getPhoneId()),
1027                         mMonthlyCount);
1028             }
1029             mDailyCount = 0;
1030             editor.putInt((KEY_DAILY_NOTIFICATION_COUNT + mPhone.getPhoneId()), mDailyCount);
1031             editor.putString((KEY_NOTIFICATION_COUNT_LAST_RESET_DATE + mPhone.getPhoneId()),
1032                     mLocalDate.toString());
1033             editor.apply();
1034         }
1035     }
1036 
getCarrierConfigs()1037     @Nullable private PersistableBundle getCarrierConfigs() {
1038         return mPhone.getContext().getSystemService(CarrierConfigManager.class)
1039                 .getConfigForSubId(mPhone.getSubId());
1040     }
1041 
getThrottleDuration(@elephonyManager.PurchasePremiumCapabilityResult int result)1042     private long getThrottleDuration(@TelephonyManager.PurchasePremiumCapabilityResult int result) {
1043         if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
1044                 || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
1045                 || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED) {
1046             return getCarrierConfigs().getLong(CarrierConfigManager
1047                     .KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
1048         }
1049         if (result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_ENTITLEMENT_CHECK_FAILED
1050                 || result == TelephonyManager.PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR) {
1051             return getCarrierConfigs().getLong(CarrierConfigManager
1052                     .KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG);
1053         }
1054         return 0;
1055     }
1056 
isPremiumCapabilitySupportedByCarrier( @elephonyManager.PremiumCapability int capability)1057     private boolean isPremiumCapabilitySupportedByCarrier(
1058             @TelephonyManager.PremiumCapability int capability) {
1059         int[] supportedCapabilities = getCarrierConfigs().getIntArray(
1060                 CarrierConfigManager.KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY);
1061         if (supportedCapabilities == null) {
1062             logd("No premium capabilities are supported by the carrier.");
1063             return false;
1064         }
1065         return Arrays.stream(supportedCapabilities)
1066                 .anyMatch(supportedCapability -> supportedCapability == capability);
1067     }
1068 
isUrlValid(@ullable String url)1069     private boolean isUrlValid(@Nullable String url) {
1070         if (!URLUtil.isValidUrl(url)) {
1071             loge("Invalid URL: " + url);
1072             return false;
1073         }
1074         if (URLUtil.isAssetUrl(url) && !url.equals(SLICE_PURCHASE_TEST_FILE)) {
1075             loge("Invalid asset: " + url);
1076             return false;
1077         }
1078         try {
1079             new URL(url).toURI();
1080         } catch (MalformedURLException | URISyntaxException e) {
1081             loge("Invalid URI: " + url);
1082             return false;
1083         }
1084         logd("Valid URL: " + url);
1085         return true;
1086     }
1087 
arePremiumCapabilitiesSupportedByDevice()1088     private boolean arePremiumCapabilitiesSupportedByDevice() {
1089         if ((mPhone.getCachedAllowedNetworkTypesBitmask()
1090                 & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
1091             logd("Premium capabilities unsupported because NR is not allowed on the device.");
1092             return false;
1093         }
1094         if (!mIsSlicingUpsellEnabled) {
1095             logd("Premium capabilities unsupported because "
1096                     + "slicing upsell is disabled on the device.");
1097         }
1098         return mIsSlicingUpsellEnabled;
1099     }
1100 
isDefaultDataSub()1101     private boolean isDefaultDataSub() {
1102         return mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId();
1103     }
1104 
1105     /**
1106      * Check whether the current network slicing configuration indicates that the given premium
1107      * capability is active and set up on the carrier network.
1108      * @param capability The premium capability to check for.
1109      * @return {@code true} if the slicing config indicates the capability is active and
1110      * {@code false} otherwise.
1111      */
1112     @VisibleForTesting
isSlicingConfigActive(@elephonyManager.PremiumCapability int capability)1113     public boolean isSlicingConfigActive(@TelephonyManager.PremiumCapability int capability) {
1114         if (mSlicingConfig == null) {
1115             return false;
1116         }
1117         for (UrspRule urspRule : mSlicingConfig.getUrspRules()) {
1118             for (TrafficDescriptor trafficDescriptor : urspRule.getTrafficDescriptors()) {
1119                 TrafficDescriptor.OsAppId osAppId =
1120                         new TrafficDescriptor.OsAppId(trafficDescriptor.getOsAppId());
1121                 if (osAppId.getAppId().equals(getAppId(capability))) {
1122                     for (RouteSelectionDescriptor rsd : urspRule.getRouteSelectionDescriptor()) {
1123                         for (NetworkSliceInfo sliceInfo : rsd.getSliceInfo()) {
1124                             if (sliceInfo.getStatus() == NetworkSliceInfo.SLICE_STATUS_ALLOWED
1125                                     && getSliceServiceTypes(capability).contains(
1126                                             sliceInfo.getSliceServiceType())) {
1127                                 return true;
1128                             }
1129                         }
1130                     }
1131                 }
1132             }
1133         }
1134         return false;
1135     }
1136 
1137     /**
1138      * Get the application ID associated with the given premium capability.
1139      * The app ID is a field in {@link TrafficDescriptor} that helps match URSP rules to determine
1140      * whether the premium capability was successfully set up on the carrier network.
1141      * @param capability The premium capability to get the app ID for.
1142      * @return The application ID associated with the premium capability.
1143      */
1144     @VisibleForTesting
getAppId(@elephonyManager.PremiumCapability int capability)1145     @NonNull public static String getAppId(@TelephonyManager.PremiumCapability int capability) {
1146         if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
1147             return "PRIORITIZE_LATENCY";
1148         }
1149         return "";
1150     }
1151 
1152     /**
1153      * Get the slice service types associated with the given premium capability.
1154      * The slice service type is a field in {@link NetworkSliceInfo} that helps to match determine
1155      * whether the premium capability was successfully set up on the carrier network.
1156      * @param capability The premium capability to get the associated slice service types for.
1157      * @return A set of slice service types associated with the premium capability.
1158      */
1159     @VisibleForTesting
getSliceServiceTypes( @elephonyManager.PremiumCapability int capability)1160     @NonNull @NetworkSliceInfo.SliceServiceType public static Set<Integer> getSliceServiceTypes(
1161             @TelephonyManager.PremiumCapability int capability) {
1162         Set<Integer> sliceServiceTypes = new HashSet<>();
1163         if (capability == TelephonyManager.PREMIUM_CAPABILITY_PRIORITIZE_LATENCY) {
1164             sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_EMBB);
1165             sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_URLLC);
1166         } else {
1167             sliceServiceTypes.add(NetworkSliceInfo.SLICE_SERVICE_TYPE_NONE);
1168         }
1169         return sliceServiceTypes;
1170     }
1171 
isNetworkAvailable()1172     private boolean isNetworkAvailable() {
1173         if (mPhone.getServiceState().getDataRoaming()) {
1174             logd("Network unavailable because device is roaming.");
1175             return false;
1176         }
1177 
1178         if (!mPhone.getDataSettingsManager().isDataEnabledForReason(
1179                 TelephonyManager.DATA_ENABLED_REASON_USER)) {
1180             logd("Network unavailable because user data is disabled.");
1181             return false;
1182         }
1183 
1184         // TODO (b/251558673): Create a listener for data network type changed to dismiss
1185         //  notification and activity when the network is no longer available.
1186         switch (mPhone.getServiceState().getDataNetworkType()) {
1187             case TelephonyManager.NETWORK_TYPE_NR:
1188                 return true;
1189             case TelephonyManager.NETWORK_TYPE_LTE:
1190             case TelephonyManager.NETWORK_TYPE_LTE_CA:
1191                 return getCarrierConfigs().getBoolean(
1192                         CarrierConfigManager.KEY_PREMIUM_CAPABILITY_SUPPORTED_ON_LTE_BOOL);
1193         }
1194         return false;
1195     }
1196 
1197     /**
1198      * Returns the failure code {@link FailureCode} as a String.
1199      *
1200      * @param failureCode The failure code.
1201      * @return The failure code as a String.
1202      */
convertFailureCodeToString(@ailureCode int failureCode)1203     @NonNull private static String convertFailureCodeToString(@FailureCode int failureCode) {
1204         switch (failureCode) {
1205             case FAILURE_CODE_UNKNOWN: return "UNKNOWN";
1206             case FAILURE_CODE_CARRIER_URL_UNAVAILABLE: return "CARRIER_URL_UNAVAILABLE";
1207             case FAILURE_CODE_AUTHENTICATION_FAILED: return "AUTHENTICATION_FAILED";
1208             case FAILURE_CODE_PAYMENT_FAILED: return "PAYMENT_FAILED";
1209             case FAILURE_CODE_NO_USER_DATA: return "NO_USER_DATA";
1210             default:
1211                 return "UNKNOWN(" + failureCode + ")";
1212         }
1213     }
1214 
reportAnomaly(@onNull String uuid, @NonNull String log)1215     private void reportAnomaly(@NonNull String uuid, @NonNull String log) {
1216         loge(log);
1217         AnomalyReporter.reportAnomaly(UUID.fromString(uuid), log);
1218     }
1219 
logd(String s)1220     private void logd(String s) {
1221         Log.d(TAG + "-" + mPhone.getPhoneId(), s);
1222     }
1223 
loge(String s)1224     private void loge(String s) {
1225         Log.e(TAG + "-" + mPhone.getPhoneId(), s);
1226     }
1227 }
1228