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