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