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