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