• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.pm.PackageManager.GET_ACTIVITIES;
20 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE;
21 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK;
22 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION;
23 import static android.net.TetheringConstants.EXTRA_TETHER_PROVISIONING_RESPONSE;
24 import static android.net.TetheringConstants.EXTRA_TETHER_SILENT_PROVISIONING_ACTION;
25 import static android.net.TetheringConstants.EXTRA_TETHER_SUBID;
26 import static android.net.TetheringConstants.EXTRA_TETHER_UI_PROVISIONING_APP_NAME;
27 import static android.net.TetheringManager.TETHERING_BLUETOOTH;
28 import static android.net.TetheringManager.TETHERING_ETHERNET;
29 import static android.net.TetheringManager.TETHERING_INVALID;
30 import static android.net.TetheringManager.TETHERING_USB;
31 import static android.net.TetheringManager.TETHERING_WIFI;
32 import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
33 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
34 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
35 
36 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
37 import static com.android.networkstack.apishim.ConstantsShim.ACTION_TETHER_UNSUPPORTED_CARRIER_UI;
38 import static com.android.networkstack.apishim.ConstantsShim.RECEIVER_NOT_EXPORTED;
39 
40 import android.annotation.NonNull;
41 import android.app.ActivityManager;
42 import android.app.AlarmManager;
43 import android.app.PendingIntent;
44 import android.content.BroadcastReceiver;
45 import android.content.ComponentName;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.content.pm.PackageManager;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.os.Parcel;
53 import android.os.ResultReceiver;
54 import android.os.SystemClock;
55 import android.os.SystemProperties;
56 import android.os.UserHandle;
57 import android.os.UserManager;
58 import android.provider.Settings;
59 import android.util.SparseIntArray;
60 
61 import androidx.annotation.Nullable;
62 
63 import com.android.internal.annotations.VisibleForTesting;
64 import com.android.modules.utils.build.SdkLevel;
65 import com.android.net.module.util.FrameworkConnectivityStatsLog;
66 import com.android.net.module.util.SharedLog;
67 
68 import java.io.PrintWriter;
69 import java.util.BitSet;
70 
71 /**
72  * Re-check tethering provisioning for enabled downstream tether types.
73  * Reference TetheringManager.TETHERING_{@code *} for each tether type.
74  *
75  * All methods of this class must be accessed from the thread of tethering
76  * state machine.
77  * @hide
78  */
79 public class EntitlementManager {
80     private static final String TAG = EntitlementManager.class.getSimpleName();
81     private static final boolean DBG = false;
82 
83     @VisibleForTesting
84     protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
85     @VisibleForTesting
86     protected static final String ACTION_PROVISIONING_ALARM =
87             "com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
88 
89     // Indicate tether provisioning is not required by carrier.
90     private static final int TETHERING_PROVISIONING_REQUIRED = 1000;
91     // Indicate tether provisioning is required by carrier.
92     private static final int TETHERING_PROVISIONING_NOT_REQUIRED = 1001;
93     // Indicate tethering is not supported by carrier.
94     private static final int TETHERING_PROVISIONING_CARRIER_UNSUPPORT = 1002;
95 
96     private static final int MS_PER_HOUR = 60 * 60 * 1000;
97     private static final int DUMP_TIMEOUT = 10_000;
98 
99     // The BitSet is the bit map of each enabled downstream types, ex:
100     // {@link TetheringManager.TETHERING_WIFI}
101     // {@link TetheringManager.TETHERING_USB}
102     // {@link TetheringManager.TETHERING_BLUETOOTH}
103     private final BitSet mCurrentDownstreams;
104     private final BitSet mExemptedDownstreams;
105     private final Context mContext;
106     private final SharedLog mLog;
107     private final SparseIntArray mEntitlementCacheValue;
108     private final Handler mHandler;
109     // Key: TetheringManager.TETHERING_*(downstream).
110     // Value: TetheringManager.TETHER_ERROR_{NO_ERROR or PROVISION_FAILED}(provisioning result).
111     private final SparseIntArray mCurrentEntitlementResults;
112     private final Runnable mPermissionChangeCallback;
113     private PendingIntent mProvisioningRecheckAlarm;
114     private boolean mLastCellularUpstreamPermitted = true;
115     private boolean mUsingCellularAsUpstream = false;
116     private boolean mNeedReRunProvisioningUi = false;
117     private OnTetherProvisioningFailedListener mListener;
118     private TetheringConfigurationFetcher mFetcher;
119     private final Dependencies mDeps;
120 
121     @VisibleForTesting(visibility = PRIVATE)
122     static class Dependencies {
123         @NonNull
124         private final Context mContext;
125         @NonNull
126         private final SharedLog mLog;
127         private final ComponentName mSilentProvisioningService;
128 
Dependencies(@onNull Context context, @NonNull SharedLog log)129         Dependencies(@NonNull Context context, @NonNull SharedLog log) {
130             mContext = context;
131             mLog = log;
132             mSilentProvisioningService = ComponentName.unflattenFromString(
133                     mContext.getResources().getString(R.string.config_wifi_tether_enable));
134         }
135 
136         /**
137          * Run the UI-enabled tethering provisioning check.
138          * @param type tethering type from TetheringManager.TETHERING_{@code *}
139          * @param receiver to receive entitlement check result.
140          *
141          * @return the broadcast intent, or null if the current user is not allowed to
142          *         perform entitlement check.
143          */
144         @Nullable
runUiTetherProvisioning(int type, final TetheringConfiguration config, ResultReceiver receiver)145         protected Intent runUiTetherProvisioning(int type, final TetheringConfiguration config,
146                 ResultReceiver receiver) {
147             if (DBG) mLog.i("runUiTetherProvisioning: " + type);
148 
149             Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING_UI);
150             intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
151             intent.putExtra(EXTRA_TETHER_UI_PROVISIONING_APP_NAME, config.provisioningApp);
152             intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
153             intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
154             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
155 
156             // Only launch entitlement UI for the current user if it is allowed to
157             // change tethering. This usually means the system user or the admin users in HSUM.
158             if (SdkLevel.isAtLeastT()) {
159                 // Create a user context for the current foreground user as UserManager#isAdmin()
160                 // operates on the context user.
161                 final int currentUserId = getCurrentUser();
162                 final UserHandle currentUser = UserHandle.of(currentUserId);
163                 final Context userContext;
164                 try {
165                     // There is no safe way to invoke this method since tethering package
166                     // might not be installed for a certain user on the OEM devices,
167                     // refer to b/382628161.
168                     userContext = mContext.createContextAsUser(currentUser, 0);
169                 } catch (IllegalStateException e) {
170                     FrameworkConnectivityStatsLog.write(
171                             FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
172                             FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_ENTITLEMENT_CREATE_CONTEXT_AS_USER_THROWS
173                     );
174                     // Fallback to startActivity if createContextAsUser failed.
175                     mLog.e("createContextAsUser failed, fallback to startActivity", e);
176                     mContext.startActivity(intent);
177                     return intent;
178                 }
179                 final UserManager userManager = userContext.getSystemService(UserManager.class);
180 
181                 if (userManager.isAdminUser()) {
182                     mContext.startActivityAsUser(intent, currentUser);
183                 } else {
184                     mLog.e("Current user (" + currentUserId
185                             + ") is not allowed to perform entitlement check.");
186                     // If the user is not allowed to perform an entitlement check
187                     // (e.g., a non-admin user), notify the receiver immediately.
188                     // This is necessary because the entitlement check app cannot
189                     // be launched to conduct the check and deliver the results.
190                     receiver.send(TETHER_ERROR_PROVISIONING_FAILED, null);
191                     return null;
192                 }
193             } else {
194                 // For T- devices, there is no other admin user other than the system user.
195                 mContext.startActivity(intent);
196             }
197             return intent;
198         }
199 
200         /**
201          * Run no UI tethering provisioning check.
202          * @param type tethering type from TetheringManager.TETHERING_{@code *}
203          */
runSilentTetherProvisioning( int type, final TetheringConfiguration config, ResultReceiver receiver)204         protected Intent runSilentTetherProvisioning(
205                 int type, final TetheringConfiguration config, ResultReceiver receiver) {
206             if (DBG) mLog.i("runSilentTetherProvisioning: " + type);
207 
208             Intent intent = new Intent();
209             intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
210             intent.putExtra(EXTRA_RUN_PROVISION, true);
211             intent.putExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION, config.provisioningAppNoUi);
212             intent.putExtra(EXTRA_TETHER_PROVISIONING_RESPONSE, config.provisioningResponse);
213             intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
214             intent.putExtra(EXTRA_TETHER_SUBID, config.activeDataSubId);
215             intent.setComponent(mSilentProvisioningService);
216             // Only admin user can change tethering and SilentTetherProvisioning don't need to
217             // show UI, it is fine to always start setting's background service as system user.
218             mContext.startService(intent);
219             return intent;
220         }
221 
222         /**
223          * Create a PendingIntent for the provisioning recheck alarm.
224          * @param pkgName the package name of the PendingIntent.
225          */
createRecheckAlarmIntent(final String pkgName)226         PendingIntent createRecheckAlarmIntent(final String pkgName) {
227             final Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
228             intent.setPackage(pkgName);
229             return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
230         }
231 
232         /**
233          * Get the current user id.
234          */
getCurrentUser()235         int getCurrentUser() {
236             return ActivityManager.getCurrentUser();
237         }
238     }
239 
EntitlementManager(Context ctx, Handler h, SharedLog log, Runnable callback)240     public EntitlementManager(Context ctx, Handler h, SharedLog log,
241             Runnable callback) {
242         this(ctx, h, log, callback, new Dependencies(ctx, log));
243     }
244 
245     @VisibleForTesting(visibility = PRIVATE)
EntitlementManager(Context ctx, Handler h, SharedLog log, Runnable callback, @NonNull Dependencies deps)246     EntitlementManager(Context ctx, Handler h, SharedLog log,
247             Runnable callback, @NonNull Dependencies deps) {
248         mContext = ctx;
249         mLog = log.forSubComponent(TAG);
250         mCurrentDownstreams = new BitSet();
251         mExemptedDownstreams = new BitSet();
252         mCurrentEntitlementResults = new SparseIntArray();
253         mEntitlementCacheValue = new SparseIntArray();
254         mPermissionChangeCallback = callback;
255         mHandler = h;
256         mDeps = deps;
257         if (SdkLevel.isAtLeastU()) {
258             mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
259                     null, mHandler, RECEIVER_NOT_EXPORTED);
260         } else {
261             mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
262                     null, mHandler);
263         }
264     }
265 
setOnTetherProvisioningFailedListener( final OnTetherProvisioningFailedListener listener)266     public void setOnTetherProvisioningFailedListener(
267             final OnTetherProvisioningFailedListener listener) {
268         mListener = listener;
269     }
270 
271     /** Callback fired when UI entitlement failed. */
272     public interface OnTetherProvisioningFailedListener {
273         /**
274          * Ui entitlement check fails in |downstream|.
275          *
276          * @param downstream tethering type from TetheringManager.TETHERING_{@code *}.
277          * @param reason Failed reason.
278          */
onTetherProvisioningFailed(int downstream, String reason)279         void onTetherProvisioningFailed(int downstream, String reason);
280     }
281 
setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher)282     public void setTetheringConfigurationFetcher(final TetheringConfigurationFetcher fetcher) {
283         mFetcher = fetcher;
284     }
285 
286     /** Interface to fetch TetheringConfiguration. */
287     public interface TetheringConfigurationFetcher {
288         /**
289          * Fetch current tethering configuration. This will be called to ensure whether entitlement
290          * check is needed.
291          * @return TetheringConfiguration instance.
292          */
fetchTetheringConfiguration()293         TetheringConfiguration fetchTetheringConfiguration();
294     }
295 
296     /**
297      * Check if cellular upstream is permitted.
298      */
isCellularUpstreamPermitted()299     public boolean isCellularUpstreamPermitted() {
300         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
301 
302         return isCellularUpstreamPermitted(config);
303     }
304 
isCellularUpstreamPermitted(final TetheringConfiguration config)305     private boolean isCellularUpstreamPermitted(final TetheringConfiguration config) {
306         // If #getTetherProvisioningCondition return TETHERING_PROVISIONING_CARRIER_UNSUPPORT,
307         // that means cellular upstream is not supported and entitlement check result is empty
308         // because entitlement check should not be run.
309         if (!isTetherProvisioningRequired(config)) return true;
310 
311         // If provisioning is required and EntitlementManager doesn't know any downstreams, cellular
312         // upstream should not be enabled. Enable cellular upstream for exempted downstreams only
313         // when there is no non-exempted downstream.
314         if (mCurrentDownstreams.isEmpty()) return !mExemptedDownstreams.isEmpty();
315 
316         return mCurrentEntitlementResults.indexOfValue(TETHER_ERROR_NO_ERROR) > -1;
317     }
318 
319     /**
320      * Set exempted downstream type. If there is only exempted downstream type active,
321      * corresponding entitlement check will not be run and cellular upstream will be permitted
322      * by default. If a privileged app enables tethering without a provisioning check, and then
323      * another app enables tethering of the same type but does not disable the provisioning check,
324      * then the downstream immediately loses exempt status and a provisioning check is run.
325      * If any non-exempted downstream type is active, the cellular upstream will be gated by the
326      * result of entitlement check from non-exempted downstreams. If entitlement check is still
327      * in progress on non-exempt downstreams, ceullar upstream would default be disabled. When any
328      * non-exempted downstream gets positive entitlement result, ceullar upstream will be enabled.
329      */
setExemptedDownstreamType(final int type)330     public void setExemptedDownstreamType(final int type) {
331         mExemptedDownstreams.set(type, true);
332     }
333 
334     /**
335      * This is called when tethering starts.
336      * Launch provisioning app if upstream is cellular.
337      *
338      * @param downstreamType tethering type from TetheringManager.TETHERING_{@code *}
339      * @param showProvisioningUi a boolean indicating whether to show the
340      *        provisioning app UI if there is one.
341      */
startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi)342     public void startProvisioningIfNeeded(int downstreamType, boolean showProvisioningUi) {
343         if (!isValidDownstreamType(downstreamType)) return;
344 
345         mCurrentDownstreams.set(downstreamType, true);
346 
347         mExemptedDownstreams.set(downstreamType, false);
348 
349         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
350         if (!isTetherProvisioningRequired(config)) return;
351 
352         // If upstream is not cellular, provisioning app would not be launched
353         // till upstream change to cellular.
354         if (mUsingCellularAsUpstream) {
355             runTetheringProvisioning(showProvisioningUi, downstreamType, config);
356             mNeedReRunProvisioningUi = false;
357         } else {
358             mNeedReRunProvisioningUi |= showProvisioningUi;
359         }
360     }
361 
362     /**
363      * Tell EntitlementManager that a given type of tethering has been disabled
364      *
365      * @param type tethering type from TetheringManager.TETHERING_{@code *}
366      */
stopProvisioningIfNeeded(int downstreamType)367     public void stopProvisioningIfNeeded(int downstreamType) {
368         if (!isValidDownstreamType(downstreamType)) return;
369 
370         mCurrentDownstreams.set(downstreamType, false);
371         // There are lurking bugs where the notion of "provisioning required" or
372         // "tethering supported" may change without without tethering being notified properly.
373         // Remove the mapping all the time no matter provisioning is required or not.
374         removeDownstreamMapping(downstreamType);
375         mExemptedDownstreams.set(downstreamType, false);
376     }
377 
378     /**
379      * Notify EntitlementManager if upstream is cellular or not.
380      *
381      * @param isCellular whether tethering upstream is cellular.
382      */
notifyUpstream(boolean isCellular)383     public void notifyUpstream(boolean isCellular) {
384         if (DBG) {
385             mLog.i("notifyUpstream: " + isCellular
386                     + ", mLastCellularUpstreamPermitted: " + mLastCellularUpstreamPermitted
387                     + ", mNeedReRunProvisioningUi: " + mNeedReRunProvisioningUi);
388         }
389         mUsingCellularAsUpstream = isCellular;
390 
391         if (mUsingCellularAsUpstream) {
392             final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
393             maybeRunProvisioning(config);
394         }
395     }
396 
397     /** Run provisioning if needed */
maybeRunProvisioning()398     public void maybeRunProvisioning() {
399         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
400         maybeRunProvisioning(config);
401     }
402 
maybeRunProvisioning(final TetheringConfiguration config)403     private void maybeRunProvisioning(final TetheringConfiguration config) {
404         if (mCurrentDownstreams.isEmpty() || !isTetherProvisioningRequired(config)) {
405             return;
406         }
407 
408         // Whenever any entitlement value changes, all downstreams will re-evaluate whether they
409         // are allowed. Therefore even if the silent check here ends in a failure and the UI later
410         // yields success, then the downstream that got a failure will re-evaluate as a result of
411         // the change and get the new correct value.
412         for (int downstream = mCurrentDownstreams.nextSetBit(0); downstream >= 0;
413                 downstream = mCurrentDownstreams.nextSetBit(downstream + 1)) {
414             // If tethering provisioning is required but entitlement check result is empty,
415             // this means tethering may need to run entitlement check or carrier network
416             // is not supported.
417             if (mCurrentEntitlementResults.indexOfKey(downstream) < 0) {
418                 runTetheringProvisioning(mNeedReRunProvisioningUi, downstream, config);
419                 mNeedReRunProvisioningUi = false;
420             }
421         }
422     }
423 
424     /**
425      * Tether provisioning has these conditions to control provisioning behavior.
426      *  1st priority : Uses system property to disable any provisioning behavior.
427      *  2nd priority : Uses {@code CarrierConfigManager#KEY_CARRIER_SUPPORTS_TETHERING_BOOL} to
428      *                 decide current carrier support cellular upstream tethering or not.
429      *                 If value is true, it means check follow up condition to know whether
430      *                 provisioning is required.
431      *                 If value is false, it means tethering could not use cellular as upstream.
432      *  3rd priority : Uses {@code CarrierConfigManager#KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL} to
433      *                 decide current carrier require the provisioning.
434      *  4th priority : Checks whether provisioning is required from RRO configuration.
435      *
436      * @param config
437      * @return integer See {@link #TETHERING_PROVISIONING_NOT_REQUIRED,
438      *                 #TETHERING_PROVISIONING_REQUIRED,
439      *                 #TETHERING_PROVISIONING_CARRIER_UNSUPPORT}
440      */
getTetherProvisioningCondition(final TetheringConfiguration config)441     private int getTetherProvisioningCondition(final TetheringConfiguration config) {
442         if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)) {
443             return TETHERING_PROVISIONING_NOT_REQUIRED;
444         }
445 
446         if (!config.isCarrierSupportTethering) {
447             // To block tethering, behave as if running provisioning check and failed.
448             return TETHERING_PROVISIONING_CARRIER_UNSUPPORT;
449         }
450 
451         if (!config.isCarrierConfigAffirmsEntitlementCheckRequired) {
452             return TETHERING_PROVISIONING_NOT_REQUIRED;
453         }
454         return (config.provisioningApp.length == 2)
455                 ? TETHERING_PROVISIONING_REQUIRED : TETHERING_PROVISIONING_NOT_REQUIRED;
456     }
457 
458     /**
459      * Check if the device requires a provisioning check in order to enable tethering.
460      *
461      * @param config an object that encapsulates the various tethering configuration elements.
462      * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
463      */
464     @VisibleForTesting
isTetherProvisioningRequired(final TetheringConfiguration config)465     protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
466         return getTetherProvisioningCondition(config) != TETHERING_PROVISIONING_NOT_REQUIRED;
467     }
468 
469     /**
470      * Confirms the need of tethering provisioning but no entitlement package exists.
471      */
isProvisioningNeededButUnavailable()472     public boolean isProvisioningNeededButUnavailable() {
473         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
474         return getTetherProvisioningCondition(config) == TETHERING_PROVISIONING_REQUIRED
475                 && !doesEntitlementPackageExist(config);
476     }
477 
doesEntitlementPackageExist(final TetheringConfiguration config)478     private boolean doesEntitlementPackageExist(final TetheringConfiguration config) {
479         final PackageManager pm = mContext.getPackageManager();
480         try {
481             pm.getPackageInfo(config.provisioningApp[0], GET_ACTIVITIES);
482         } catch (PackageManager.NameNotFoundException e) {
483             return false;
484         }
485         return true;
486     }
487 
488     /**
489      * Re-check tethering provisioning for all enabled tether types.
490      * Reference TetheringManager.TETHERING_{@code *} for each tether type.
491      *
492      * @param config an object that encapsulates the various tethering configuration elements.
493      * Note: this method is only called from @{link Tethering.TetherMainSM} on the handler thread.
494      * If there are new callers from different threads, the logic should move to
495      * @{link Tethering.TetherMainSM} handler to avoid race conditions.
496      */
reevaluateSimCardProvisioning(final TetheringConfiguration config)497     public void reevaluateSimCardProvisioning(final TetheringConfiguration config) {
498         if (DBG) mLog.i("reevaluateSimCardProvisioning");
499 
500         if (!mHandler.getLooper().isCurrentThread()) {
501             // Except for test, this log should not appear in normal flow.
502             mLog.log("reevaluateSimCardProvisioning() don't run in TetherMainSM thread");
503         }
504         mEntitlementCacheValue.clear();
505         mCurrentEntitlementResults.clear();
506 
507         if (!isTetherProvisioningRequired(config)) {
508             evaluateCellularPermission(config);
509             return;
510         }
511 
512         if (mUsingCellularAsUpstream) {
513             maybeRunProvisioning(config);
514         }
515     }
516 
runTetheringProvisioning( boolean showProvisioningUi, int downstreamType, final TetheringConfiguration config)517     private void runTetheringProvisioning(
518             boolean showProvisioningUi, int downstreamType, final TetheringConfiguration config) {
519         if (!config.isCarrierSupportTethering) {
520             mListener.onTetherProvisioningFailed(downstreamType, "Carrier does not support.");
521             if (showProvisioningUi) {
522                 showCarrierUnsupportedDialog();
523             }
524             return;
525         }
526 
527         ResultReceiver receiver =
528                 buildProxyReceiver(downstreamType, showProvisioningUi/* notifyFail */, null);
529         if (showProvisioningUi) {
530             mDeps.runUiTetherProvisioning(downstreamType, config, receiver);
531         } else {
532             mDeps.runSilentTetherProvisioning(downstreamType, config, receiver);
533         }
534     }
535 
showCarrierUnsupportedDialog()536     private void showCarrierUnsupportedDialog() {
537         // This is only used when TetheringConfiguration.isCarrierSupportTethering is false.
538         if (!SdkLevel.isAtLeastT()) {
539             return;
540         }
541         Intent intent = new Intent(ACTION_TETHER_UNSUPPORTED_CARRIER_UI);
542         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
543         mContext.startActivity(intent);
544     }
545 
546     // Not needed to check if this don't run on the handler thread because it's private.
scheduleProvisioningRecheck(final TetheringConfiguration config)547     private void scheduleProvisioningRecheck(final TetheringConfiguration config) {
548         if (mProvisioningRecheckAlarm == null) {
549             final int period = config.provisioningCheckPeriod;
550             if (period <= 0) return;
551 
552             mProvisioningRecheckAlarm = mDeps.createRecheckAlarmIntent(mContext.getPackageName());
553             AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
554                     Context.ALARM_SERVICE);
555             long triggerAtMillis = SystemClock.elapsedRealtime() + (period * MS_PER_HOUR);
556             alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis,
557                     mProvisioningRecheckAlarm);
558         }
559     }
560 
cancelTetherProvisioningRechecks()561     private void cancelTetherProvisioningRechecks() {
562         if (mProvisioningRecheckAlarm != null) {
563             AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
564                     Context.ALARM_SERVICE);
565             alarmManager.cancel(mProvisioningRecheckAlarm);
566             mProvisioningRecheckAlarm = null;
567         }
568     }
569 
rescheduleProvisioningRecheck(final TetheringConfiguration config)570     private void rescheduleProvisioningRecheck(final TetheringConfiguration config) {
571         cancelTetherProvisioningRechecks();
572         scheduleProvisioningRecheck(config);
573     }
574 
evaluateCellularPermission(final TetheringConfiguration config)575     private void evaluateCellularPermission(final TetheringConfiguration config) {
576         final boolean permitted = isCellularUpstreamPermitted(config);
577 
578         if (DBG) {
579             mLog.i("Cellular permission change from " + mLastCellularUpstreamPermitted
580                     + " to " + permitted);
581         }
582 
583         if (mLastCellularUpstreamPermitted != permitted) {
584             mLog.log("Cellular permission change: " + permitted);
585             mPermissionChangeCallback.run();
586         }
587         // Only schedule periodic re-check when tether is provisioned
588         // and the result is ok.
589         if (permitted && mCurrentEntitlementResults.size() > 0) {
590             scheduleProvisioningRecheck(config);
591         } else {
592             cancelTetherProvisioningRechecks();
593         }
594         mLastCellularUpstreamPermitted = permitted;
595     }
596 
597     /**
598      * Add the mapping between provisioning result and tethering type.
599      * Notify UpstreamNetworkMonitor if Cellular permission changes.
600      *
601      * @param type tethering type from TetheringManager.TETHERING_{@code *}
602      * @param resultCode Provisioning result
603      */
addDownstreamMapping(int type, int resultCode)604     protected void addDownstreamMapping(int type, int resultCode) {
605         mLog.i("addDownstreamMapping: " + type + ", result: " + resultCode
606                 + " ,TetherTypeRequested: " + mCurrentDownstreams.get(type));
607         if (!mCurrentDownstreams.get(type)) return;
608 
609         mCurrentEntitlementResults.put(type, resultCode);
610         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
611         evaluateCellularPermission(config);
612     }
613 
614     /**
615      * Remove the mapping for input tethering type.
616      * @param type tethering type from TetheringManager.TETHERING_{@code *}
617      */
removeDownstreamMapping(int type)618     protected void removeDownstreamMapping(int type) {
619         mLog.i("removeDownstreamMapping: " + type);
620         mCurrentEntitlementResults.delete(type);
621         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
622         evaluateCellularPermission(config);
623     }
624 
625     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
626         @Override
627         public void onReceive(Context context, Intent intent) {
628             if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) {
629                 mLog.log("Received provisioning alarm");
630                 final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
631                 rescheduleProvisioningRecheck(config);
632                 reevaluateSimCardProvisioning(config);
633             }
634         }
635     };
636 
isValidDownstreamType(int type)637     private static boolean isValidDownstreamType(int type) {
638         switch (type) {
639             case TETHERING_BLUETOOTH:
640             case TETHERING_ETHERNET:
641             case TETHERING_USB:
642             case TETHERING_WIFI:
643                 return true;
644             default:
645                 return false;
646         }
647     }
648 
649     /**
650      * Dump the infromation of EntitlementManager.
651      * @param pw {@link PrintWriter} is used to print formatted
652      */
dump(PrintWriter pw)653     public void dump(PrintWriter pw) {
654         pw.print("isCellularUpstreamPermitted: ");
655         pw.println(isCellularUpstreamPermitted());
656         for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
657                 type = mCurrentDownstreams.nextSetBit(type + 1)) {
658             pw.print("Type: ");
659             pw.print(typeString(type));
660             if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
661                 pw.print(", Value: ");
662                 pw.println(errorString(mCurrentEntitlementResults.get(type)));
663             } else {
664                 pw.println(", Value: empty");
665             }
666         }
667         pw.print("Exempted: [");
668         for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0;
669                 type = mExemptedDownstreams.nextSetBit(type + 1)) {
670             pw.print(typeString(type));
671             pw.print(", ");
672         }
673         pw.println("]");
674     }
675 
typeString(int type)676     private static String typeString(int type) {
677         switch (type) {
678             case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH";
679             case TETHERING_INVALID: return "TETHERING_INVALID";
680             case TETHERING_USB: return "TETHERING_USB";
681             case TETHERING_WIFI: return "TETHERING_WIFI";
682             default:
683                 return String.format("TETHERING UNKNOWN TYPE (%d)", type);
684         }
685     }
686 
errorString(int value)687     private static String errorString(int value) {
688         switch (value) {
689             case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN";
690             case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR";
691             case TETHER_ERROR_PROVISIONING_FAILED: return "TETHER_ERROR_PROVISIONING_FAILED";
692             default:
693                 return String.format("UNKNOWN ERROR (%d)", value);
694         }
695     }
696 
buildProxyReceiver(int type, boolean notifyFail, final ResultReceiver receiver)697     private ResultReceiver buildProxyReceiver(int type, boolean notifyFail,
698             final ResultReceiver receiver) {
699         ResultReceiver rr = new ResultReceiver(mHandler) {
700             @Override
701             protected void onReceiveResult(int resultCode, Bundle resultData) {
702                 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
703                 addDownstreamMapping(type, updatedCacheValue);
704                 if (updatedCacheValue == TETHER_ERROR_PROVISIONING_FAILED && notifyFail) {
705                     mListener.onTetherProvisioningFailed(
706                             type, "Tethering provisioning failed.");
707                 }
708                 if (receiver != null) receiver.send(updatedCacheValue, null);
709             }
710         };
711 
712         return writeToParcel(rr);
713     }
714 
715     // Instances of ResultReceiver need to be public classes for remote processes to be able
716     // to load them (otherwise, ClassNotFoundException). For private classes, this method
717     // performs a trick : round-trip parceling any instance of ResultReceiver will return a
718     // vanilla instance of ResultReceiver sharing the binder token with the original receiver.
719     // The binder token has a reference to the original instance of the private class and will
720     // still call its methods, and can be sent over. However it cannot be used for anything
721     // else than sending over a Binder call.
722     // While round-trip parceling is not great, there is currently no other way of generating
723     // a vanilla instance of ResultReceiver because all its fields are private.
writeToParcel(final ResultReceiver receiver)724     private ResultReceiver writeToParcel(final ResultReceiver receiver) {
725         Parcel parcel = Parcel.obtain();
726         receiver.writeToParcel(parcel, 0);
727         parcel.setDataPosition(0);
728         ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
729         parcel.recycle();
730         return receiverForSending;
731     }
732 
733     /**
734      * Update the last entitlement value to internal cache
735      *
736      * @param type tethering type from TetheringManager.TETHERING_{@code *}
737      * @param resultCode last entitlement value
738      * @return the last updated entitlement value
739      */
updateEntitlementCacheValue(int type, int resultCode)740     private int updateEntitlementCacheValue(int type, int resultCode) {
741         if (DBG) {
742             mLog.i("updateEntitlementCacheValue: " + type + ", result: " + resultCode);
743         }
744         if (resultCode == TETHER_ERROR_NO_ERROR) {
745             mEntitlementCacheValue.put(type, resultCode);
746             return resultCode;
747         } else {
748             mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISIONING_FAILED);
749             return TETHER_ERROR_PROVISIONING_FAILED;
750         }
751     }
752 
753     /** Get the last value of the tethering entitlement check. */
requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver, boolean showEntitlementUi)754     public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
755             boolean showEntitlementUi) {
756         if (!isValidDownstreamType(downstream)) {
757             receiver.send(TETHER_ERROR_ENTITLEMENT_UNKNOWN, null);
758             return;
759         }
760 
761         final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
762 
763         switch (getTetherProvisioningCondition(config)) {
764             case TETHERING_PROVISIONING_NOT_REQUIRED:
765                 receiver.send(TETHER_ERROR_NO_ERROR, null);
766                 return;
767             case TETHERING_PROVISIONING_CARRIER_UNSUPPORT:
768                 receiver.send(TETHER_ERROR_PROVISIONING_FAILED, null);
769                 return;
770         }
771 
772         final int cacheValue = mEntitlementCacheValue.get(
773                 downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN);
774         if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
775             receiver.send(cacheValue, null);
776         } else {
777             ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
778             mDeps.runUiTetherProvisioning(downstream, config, proxy);
779         }
780     }
781 }
782