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