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