1 /* 2 * Copyright (C) 2018 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.networkstack.tethering; 18 19 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; 20 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; 21 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; 22 import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE; 23 import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION; 24 import static android.net.TetheringConstants.EXTRA_TETHER_SUBID; 25 import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME; 26 import static android.net.TetheringManager.TETHERING_BLUETOOTH; 27 import static android.net.TetheringManager.TETHERING_ETHERNET; 28 import static android.net.TetheringManager.TETHERING_INVALID; 29 import static android.net.TetheringManager.TETHERING_USB; 30 import static android.net.TetheringManager.TETHERING_WIFI; 31 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; 32 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; 33 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; 34 35 import android.app.AlarmManager; 36 import android.app.PendingIntent; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.net.util.SharedLog; 43 import android.os.Bundle; 44 import android.os.Handler; 45 import android.os.Parcel; 46 import android.os.PersistableBundle; 47 import android.os.ResultReceiver; 48 import android.os.SystemClock; 49 import android.os.SystemProperties; 50 import android.provider.Settings; 51 import android.telephony.CarrierConfigManager; 52 import android.util.SparseIntArray; 53 54 import com.android.internal.annotations.VisibleForTesting; 55 56 import java.io.PrintWriter; 57 import java.util.BitSet; 58 59 /** 60 * Re-check tethering provisioning for enabled downstream tether types. 61 * Reference TetheringManager.TETHERING_{@code *} for each tether type. 62 * 63 * All methods of this class must be accessed from the thread of tethering 64 * state machine. 65 * @hide 66 */ 67 public class EntitlementManager { 68 private static final String TAG = EntitlementManager.class.getSimpleName(); 69 private static final boolean DBG = false; 70 71 @VisibleForTesting 72 protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; 73 private static final String ACTION_PROVISIONING_ALARM = 74 "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM"; 75 76 private final ComponentName mSilentProvisioningService; 77 private static final int MS_PER_HOUR = 60 * 60 * 1000; 78 private static final int DUMP_TIMEOUT = 10_000; 79 80 // The BitSet is the bit map of each enabled downstream types, ex: 81 // {@link TetheringManager.TETHERING_WIFI} 82 // {@link TetheringManager.TETHERING_USB} 83 // {@link TetheringManager.TETHERING_BLUETOOTH} 84 private final BitSet mCurrentDownstreams; 85 private final BitSet mExemptedDownstreams; 86 private final Context mContext; 87 private final SharedLog mLog; 88 private final SparseIntArray mEntitlementCacheValue; 89 private final Handler mHandler; 90 // Key: TetheringManager.TETHERING_*(downstream). 91 // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result). 92 private final SparseIntArray mCurrentEntitlementResults; 93 private final Runnable mPermissionChangeCallback; 94 private PendingIntent mProvisioningRecheckAlarm; 95 private boolean mLastCellularUpstreamPermitted = true; 96 private boolean mUsingCellularAsUpstream = false; 97 private boolean mNeedReRunProvisioningUi = false; 98 private OnUiEntitlementFailedListener mListener; 99 private TetheringConfigurationFetcher mFetcher; 100 EntitlementManager(Context ctx, Handler h, SharedLog log, Runnable callback)101 public EntitlementManager(Context ctx, Handler h, SharedLog log, 102 Runnable callback) { 103 mContext = ctx; 104 mLog = log.forSubComponent(TAG); 105 mCurrentDownstreams = new BitSet(); 106 mExemptedDownstreams = new BitSet(); 107 mCurrentEntitlementResults = new SparseIntArray(); 108 mEntitlementCacheValue = new SparseIntArray(); 109 mPermissionChangeCallback = callback; 110 mHandler = h; 111 mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM), 112 null, mHandler); 113 mSilentProvisioningService = ComponentName.unflattenFromString( 114 mContext.getResources().getString(R.string.config_wifi_tether_enable)); 115 } 116 setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener)117 public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) { 118 mListener = listener; 119 } 120 121 /** Callback fired when UI entitlement failed. */ 122 public interface OnUiEntitlementFailedListener { 123 /** 124 * Ui entitlement check fails in |downstream|. 125 * 126 * @param downstream tethering type from TetheringManager.TETHERING_{@code *}. 127 */ onUiEntitlementFailed(int downstream)128 void onUiEntitlementFailed(int downstream); 129 } 130 setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher)131 public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) { 132 mFetcher = fetcher; 133 } 134 135 /** Interface to fetch TetheringConfiguration. */ 136 public interface TetheringConfigurationFetcher { 137 /** 138 * Fetch current tethering configuration. This will be called to ensure whether entitlement 139 * check is needed. 140 * @return TetheringConfiguration instance. 141 */ fetchTetheringConfiguration()142 TetheringConfiguration fetchTetheringConfiguration(); 143 } 144 145 /** 146 * Check if cellular upstream is permitted. 147 */ isCellularUpstreamPermitted()148 public boolean isCellularUpstreamPermitted() { 149 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 150 151 return isCellularUpstreamPermitted(config); 152 } 153 isCellularUpstreamPermitted(final TetheringConfiguration config)154 private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) { 155 if (!isTetherProvisioningRequired(config)) return true; 156 157 // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular 158 // upstream should not be enabled. Enable cellular upstream for exempted downstreams only 159 // when there is no non-exempted downstream. 160 if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty(); 161 162 return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1; 163 } 164 165 /** 166 * Set exempted downstream type. If there is only exempted downstream type active, 167 * corresponding entitlement check will not be run and cellular upstream will be permitted 168 * by default. If a privileged app enables tethering without a provisioning check, and then 169 * another app enables tethering of the same type but does not disable the provisioning check, 170 * then the downstream immediately loses exempt status and a provisioning check is run. 171 * If any non-exempted downstream type is active, the cellular upstream will be gated by the 172 * result of entitlement check from non-exempted downstreams. If entitlement check is still 173 * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any 174 * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled. 175 */ setExemptedDownstreamType(final int type)176 public void setExemptedDownstreamType(final int type) { 177 mExemptedDownstreams.set(type, true); 178 } 179 180 /** 181 * This is called when tethering starts. 182 * Launch provisioning app if upstream is cellular. 183 * 184 * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *} 185 * @param showProvisioningUi a boolean indicating whether to show the 186 * provisioning app UI if there is one. 187 */ startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi)188 public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) { 189 if (!isValidDownstreamType(downstreamType)) return; 190 191 mCurrentDownstreams.set(downstreamType, true); 192 193 mExemptedDownstreams.set(downstreamType, false); 194 195 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 196 if (!isTetherProvisioningRequired(config)) return; 197 198 // If upstream is not cellular, provisioning app would not be launched 199 // till upstream change to cellular. 200 if (mUsingCellularAsUpstream) { 201 if (showProvisioningUi) { 202 runUiTetherProvisioning(downstreamType, config); 203 } else { 204 runSilentTetherProvisioning(downstreamType, config); 205 } 206 mNeedReRunProvisioningUi = false; 207 } else { 208 mNeedReRunProvisioningUi |= showProvisioningUi; 209 } 210 } 211 212 /** 213 * Tell EntitlementManager that a given type of tethering has been disabled 214 * 215 * @param type tethering type from TetheringManager.TETHERING_{@code *} 216 */ stopProvisioningIfNeeded(int downstreamType)217 public void stopProvisioningIfNeeded(int downstreamType) { 218 if (!isValidDownstreamType(downstreamType)) return; 219 220 mCurrentDownstreams.set(downstreamType, false); 221 // There are lurking bugs where the notion of "provisioning required" or 222 // "tethering supported" may change without without tethering being notified properly. 223 // Remove the mapping all the time no matter provisioning is required or not. 224 removeDownstreamMapping(downstreamType); 225 mExemptedDownstreams.set(downstreamType, false); 226 } 227 228 /** 229 * Notify EntitlementManager if upstream is cellular or not. 230 * 231 * @param isCellular whether tethering upstream is cellular. 232 */ notifyUpstream(boolean isCellular)233 public void notifyUpstream(boolean isCellular) { 234 if (DBG) { 235 mLog.i("notifyUpstream: " + isCellular 236 + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted 237 + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi); 238 } 239 mUsingCellularAsUpstream = isCellular; 240 241 if (mUsingCellularAsUpstream) { 242 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 243 maybeRunProvisioning(config); 244 } 245 } 246 247 /** Run provisioning if needed */ maybeRunProvisioning()248 public void maybeRunProvisioning() { 249 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 250 maybeRunProvisioning(config); 251 } 252 maybeRunProvisioning(final TetheringConfiguration config)253 private void maybeRunProvisioning(final TetheringConfiguration config) { 254 if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) { 255 return; 256 } 257 258 // Whenever any entitlement value changes, all downstreams will re-evaluate whether they 259 // are allowed. Therefore even if the silent check here ends in a failure and the UI later 260 // yields success, then the downstream that got a failure will re-evaluate as a result of 261 // the change and get the new correct value. 262 for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0; 263 downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) { 264 if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) { 265 if (mNeedReRunProvisioningUi) { 266 mNeedReRunProvisioningUi = false; 267 runUiTetherProvisioning(downstream, config); 268 } else { 269 runSilentTetherProvisioning(downstream, config); 270 } 271 } 272 } 273 } 274 275 /** 276 * Check if the device requires a provisioning check in order to enable tethering. 277 * 278 * @param config an object that encapsulates the various tethering configuration elements. 279 * @return a boolean - {@code true} indicating tether provisioning is required by the carrier. 280 */ 281 @VisibleForTesting isTetherProvisioningRequired(final TetheringConfiguration config)282 protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) { 283 if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false) 284 || config.provisioningApp.length == 0) { 285 return false; 286 } 287 if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) { 288 return false; 289 } 290 return (config.provisioningApp.length == 2); 291 } 292 293 /** 294 * Re-check tethering provisioning for all enabled tether types. 295 * Reference TetheringManager.TETHERING_{@code *} for each tether type. 296 * 297 * @param config an object that encapsulates the various tethering configuration elements. 298 * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread. 299 * If there are new callers from different threads, the logic should move to 300 * @{link Tethering.TetherMainSM} handler to avoid race conditions. 301 */ reevaluateSimCardProvisioning(final TetheringConfiguration config)302 public void reevaluateSimCardProvisioning(final TetheringConfiguration config) { 303 if (DBG) mLog.i("reevaluateSimCardProvisioning"); 304 305 if (!mHandler.getLooper().isCurrentThread()) { 306 // Except for test, this log should not appear in normal flow. 307 mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread"); 308 } 309 mEntitlementCacheValue.clear(); 310 mCurrentEntitlementResults.clear(); 311 312 // TODO: refine provisioning check to isTetherProvisioningRequired() ?? 313 if (!config.hasMobileHotspotProvisionApp() 314 || carrierConfigAffirmsEntitlementCheckNotRequired(config)) { 315 evaluateCellularPermission(config); 316 return; 317 } 318 319 if (mUsingCellularAsUpstream) { 320 maybeRunProvisioning(config); 321 } 322 } 323 324 /** 325 * Get carrier configuration bundle. 326 * @param config an object that encapsulates the various tethering configuration elements. 327 * */ getCarrierConfig(final TetheringConfiguration config)328 public PersistableBundle getCarrierConfig(final TetheringConfiguration config) { 329 final CarrierConfigManager configManager = (CarrierConfigManager) mContext 330 .getSystemService(Context.CARRIER_CONFIG_SERVICE); 331 if (configManager == null) return null; 332 333 final PersistableBundle carrierConfig = configManager.getConfigForSubId( 334 config.activeDataSubId); 335 336 if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) { 337 return carrierConfig; 338 } 339 340 return null; 341 } 342 343 // The logic here is aimed solely at confirming that a CarrierConfig exists 344 // and affirms that entitlement checks are not required. 345 // 346 // TODO: find a better way to express this, or alter the checking process 347 // entirely so that this is more intuitive. carrierConfigAffirmsEntitlementCheckNotRequired( final TetheringConfiguration config)348 private boolean carrierConfigAffirmsEntitlementCheckNotRequired( 349 final TetheringConfiguration config) { 350 // Check carrier config for entitlement checks 351 final PersistableBundle carrierConfig = getCarrierConfig(config); 352 if (carrierConfig == null) return false; 353 354 // A CarrierConfigManager was found and it has a config. 355 final boolean isEntitlementCheckRequired = carrierConfig.getBoolean( 356 CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); 357 return !isEntitlementCheckRequired; 358 } 359 360 /** 361 * Run no UI tethering provisioning check. 362 * @param type tethering type from TetheringManager.TETHERING_{@code *} 363 * @param subId default data subscription ID. 364 */ 365 @VisibleForTesting runSilentTetherProvisioning(int type, final TetheringConfiguration config)366 protected Intent runSilentTetherProvisioning(int type, final TetheringConfiguration config) { 367 if (DBG) mLog.i("runSilentTetherProvisioning: " + type); 368 // For silent provisioning, settings would stop tethering when entitlement fail. 369 ResultReceiver receiver = buildProxyReceiver(type, false/* notifyFail */, null); 370 371 Intent intent = new Intent(); 372 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); 373 intent.putExtra(EXTRA_RUN_PROVISION, true); 374 intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi); 375 intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse); 376 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); 377 intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); 378 intent.setComponent(mSilentProvisioningService); 379 // Only admin user can change tethering and SilentTetherProvisioning don't need to 380 // show UI, it is fine to always start setting's background service as system user. 381 mContext.startService(intent); 382 return intent; 383 } 384 runUiTetherProvisioning(int type, final TetheringConfiguration config)385 private void runUiTetherProvisioning(int type, final TetheringConfiguration config) { 386 ResultReceiver receiver = buildProxyReceiver(type, true/* notifyFail */, null); 387 runUiTetherProvisioning(type, config, receiver); 388 } 389 390 /** 391 * Run the UI-enabled tethering provisioning check. 392 * @param type tethering type from TetheringManager.TETHERING_{@code *} 393 * @param subId default data subscription ID. 394 * @param receiver to receive entitlement check result. 395 */ 396 @VisibleForTesting runUiTetherProvisioning(int type, final TetheringConfiguration config, ResultReceiver receiver)397 protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config, 398 ResultReceiver receiver) { 399 if (DBG) mLog.i("runUiTetherProvisioning: " + type); 400 401 Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI); 402 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type); 403 intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp); 404 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver); 405 intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId); 406 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 407 // Only launch entitlement UI for system user. Entitlement UI should not appear for other 408 // user because only admin user is allowed to change tethering. 409 mContext.startActivity(intent); 410 return intent; 411 } 412 413 // Not needed to check if this don't run on the handler thread because it's private. scheduleProvisioningRechecks(final TetheringConfiguration config)414 private void scheduleProvisioningRechecks(final TetheringConfiguration config) { 415 if (mProvisioningRecheckAlarm == null) { 416 final int period = config.provisioningCheckPeriod; 417 if (period <= 0) return; 418 419 Intent intent = new Intent(ACTION_PROVISIONING_ALARM); 420 mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent, 421 PendingIntent.FLAG_IMMUTABLE); 422 AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( 423 Context.ALARM_SERVICE); 424 long periodMs = period * MS_PER_HOUR; 425 long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs; 426 alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs, 427 mProvisioningRecheckAlarm); 428 } 429 } 430 cancelTetherProvisioningRechecks()431 private void cancelTetherProvisioningRechecks() { 432 if (mProvisioningRecheckAlarm != null) { 433 AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( 434 Context.ALARM_SERVICE); 435 alarmManager.cancel(mProvisioningRecheckAlarm); 436 mProvisioningRecheckAlarm = null; 437 } 438 } 439 evaluateCellularPermission(final TetheringConfiguration config)440 private void evaluateCellularPermission(final TetheringConfiguration config) { 441 final boolean permitted = isCellularUpstreamPermitted(config); 442 443 if (DBG) { 444 mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted 445 + " to " + permitted); 446 } 447 448 if (mLastCellularUpstreamPermitted != permitted) { 449 mLog.log("Cellular permission change: " + permitted); 450 mPermissionChangeCallback.run(); 451 } 452 // Only schedule periodic re-check when tether is provisioned 453 // and the result is ok. 454 if (permitted && mCurrentEntitlementResults.size() > 0) { 455 scheduleProvisioningRechecks(config); 456 } else { 457 cancelTetherProvisioningRechecks(); 458 } 459 mLastCellularUpstreamPermitted = permitted; 460 } 461 462 /** 463 * Add the mapping between provisioning result and tethering type. 464 * Notify UpstreamNetworkMonitor if Cellular permission changes. 465 * 466 * @param type tethering type from TetheringManager.TETHERING_{@code *} 467 * @param resultCode Provisioning result 468 */ addDownstreamMapping(int type, int resultCode)469 protected void addDownstreamMapping(int type, int resultCode) { 470 mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode 471 + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type)); 472 if (!mCurrentDownstreams.get(type)) return; 473 474 mCurrentEntitlementResults.put(type, resultCode); 475 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 476 evaluateCellularPermission(config); 477 } 478 479 /** 480 * Remove the mapping for input tethering type. 481 * @param type tethering type from TetheringManager.TETHERING_{@code *} 482 */ removeDownstreamMapping(int type)483 protected void removeDownstreamMapping(int type) { 484 mLog.i("removeDownstreamMapping: " + type); 485 mCurrentEntitlementResults.delete(type); 486 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 487 evaluateCellularPermission(config); 488 } 489 490 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 491 @Override 492 public void onReceive(Context context, Intent intent) { 493 if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) { 494 mLog.log("Received provisioning alarm"); 495 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 496 reevaluateSimCardProvisioning(config); 497 } 498 } 499 }; 500 isValidDownstreamType(int type)501 private static boolean isValidDownstreamType(int type) { 502 switch (type) { 503 case TETHERING_BLUETOOTH: 504 case TETHERING_ETHERNET: 505 case TETHERING_USB: 506 case TETHERING_WIFI: 507 return true; 508 default: 509 return false; 510 } 511 } 512 513 /** 514 * Dump the infromation of EntitlementManager. 515 * @param pw {@link PrintWriter} is used to print formatted 516 */ dump(PrintWriter pw)517 public void dump(PrintWriter pw) { 518 pw.print("isCellularUpstreamPermitted: "); 519 pw.println(isCellularUpstreamPermitted()); 520 for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0; 521 type = mCurrentDownstreams.nextSetBit(type + 1)) { 522 pw.print("Type: "); 523 pw.print(typeString(type)); 524 if (mCurrentEntitlementResults.indexOfKey(type) > -1) { 525 pw.print(", Value: "); 526 pw.println(errorString(mCurrentEntitlementResults.get(type))); 527 } else { 528 pw.println(", Value: empty"); 529 } 530 } 531 pw.print("Exempted: ["); 532 for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0; 533 type = mExemptedDownstreams.nextSetBit(type + 1)) { 534 pw.print(typeString(type)); 535 pw.print(", "); 536 } 537 pw.println("]"); 538 } 539 typeString(int type)540 private static String typeString(int type) { 541 switch (type) { 542 case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH"; 543 case TETHERING_INVALID: return "TETHERING_INVALID"; 544 case TETHERING_USB: return "TETHERING_USB"; 545 case TETHERING_WIFI: return "TETHERING_WIFI"; 546 default: 547 return String.format("TETHERING UNKNOWN TYPE (%d)", type); 548 } 549 } 550 errorString(int value)551 private static String errorString(int value) { 552 switch (value) { 553 case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; 554 case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; 555 case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED"; 556 default: 557 return String.format("UNKNOWN ERROR (%d)", value); 558 } 559 } 560 buildProxyReceiver(int type, boolean notifyFail, final ResultReceiver receiver)561 private ResultReceiver buildProxyReceiver(int type, boolean notifyFail, 562 final ResultReceiver receiver) { 563 ResultReceiver rr = new ResultReceiver(mHandler) { 564 @Override 565 protected void onReceiveResult(int resultCode, Bundle resultData) { 566 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode); 567 addDownstreamMapping(type, updatedCacheValue); 568 if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) { 569 mListener.onUiEntitlementFailed(type); 570 } 571 if (receiver != null) receiver.send(updatedCacheValue, null); 572 } 573 }; 574 575 return writeToParcel(rr); 576 } 577 578 // Instances of ResultReceiver need to be public classes for remote processes to be able 579 // to load them (otherwise, ClassNotFoundException). For private classes, this method 580 // performs a trick : round-trip parceling any instance of ResultReceiver will return a 581 // vanilla instance of ResultReceiver sharing the binder token with the original receiver. 582 // The binder token has a reference to the original instance of the private class and will 583 // still call its methods, and can be sent over. However it cannot be used for anything 584 // else than sending over a Binder call. 585 // While round-trip parceling is not great, there is currently no other way of generating 586 // a vanilla instance of ResultReceiver because all its fields are private. writeToParcel(final ResultReceiver receiver)587 private ResultReceiver writeToParcel(final ResultReceiver receiver) { 588 Parcel parcel = Parcel.obtain(); 589 receiver.writeToParcel(parcel, 0); 590 parcel.setDataPosition(0); 591 ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel); 592 parcel.recycle(); 593 return receiverForSending; 594 } 595 596 /** 597 * Update the last entitlement value to internal cache 598 * 599 * @param type tethering type from TetheringManager.TETHERING_{@code *} 600 * @param resultCode last entitlement value 601 * @return the last updated entitlement value 602 */ updateEntitlementCacheValue(int type, int resultCode)603 private int updateEntitlementCacheValue(int type, int resultCode) { 604 if (DBG) { 605 mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode); 606 } 607 if (resultCode == TETHER_ERROR_NO_ERROR) { 608 mEntitlementCacheValue.put(type, resultCode); 609 return resultCode; 610 } else { 611 mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED); 612 return TETHER_ERROR_PROVISIONING_FAILED; 613 } 614 } 615 616 /** Get the last value of the tethering entitlement check. */ requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, boolean showEntitlementUi)617 public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, 618 boolean showEntitlementUi) { 619 if (!isValidDownstreamType(downstream)) { 620 receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null); 621 return; 622 } 623 624 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration(); 625 if (!isTetherProvisioningRequired(config)) { 626 receiver.send(TETHER_ERROR_NO_ERROR, null); 627 return; 628 } 629 630 final int cacheValue = mEntitlementCacheValue.get( 631 downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN); 632 if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { 633 receiver.send(cacheValue, null); 634 } else { 635 ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver); 636 runUiTetherProvisioning(downstream, config, proxy); 637 } 638 } 639 } 640