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