blob: a4e3e1d85bcbfbcb0a3558d99f66268beb02a8ea [file] [log] [blame]
markchienb6eb2c22018-07-18 14:29:20 +08001/*
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
17package com.android.server.connectivity.tethering;
18
19import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
20import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
21import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
22import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
23import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
24
25import static com.android.internal.R.string.config_wifi_tether_enable;
26
27import android.annotation.Nullable;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
31import android.content.res.Resources;
32import android.net.util.SharedLog;
33import android.os.Binder;
34import android.os.PersistableBundle;
35import android.os.ResultReceiver;
36import android.os.UserHandle;
37import android.provider.Settings;
38import android.telephony.CarrierConfigManager;
39import android.util.ArraySet;
40
41import com.android.internal.annotations.GuardedBy;
42import com.android.internal.annotations.VisibleForTesting;
43import com.android.server.connectivity.MockableSystemProperties;
44
45/**
46 * This class encapsulates entitlement/provisioning mechanics
47 * provisioning check only applies to the use of the mobile network as an upstream
48 *
49 * @hide
50 */
51public class EntitlementManager {
52 private static final String TAG = EntitlementManager.class.getSimpleName();
53
54 // {@link ComponentName} of the Service used to run tether provisioning.
55 private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
56 Resources.getSystem().getString(config_wifi_tether_enable));
57 protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
58
59 // The ArraySet contains enabled downstream types, ex:
60 // {@link ConnectivityManager.TETHERING_WIFI}
61 // {@link ConnectivityManager.TETHERING_USB}
62 // {@link ConnectivityManager.TETHERING_BLUETOOTH}
63 @GuardedBy("mCurrentTethers")
64 private final ArraySet<Integer> mCurrentTethers;
65 private final Context mContext;
66 private final MockableSystemProperties mSystemProperties;
67 private final SharedLog mLog;
68 @Nullable
69 private TetheringConfiguration mConfig;
70
71 public EntitlementManager(Context ctx, SharedLog log,
72 MockableSystemProperties systemProperties) {
73 mContext = ctx;
74 mLog = log;
75 mCurrentTethers = new ArraySet<Integer>();
76 mSystemProperties = systemProperties;
77 }
78
79 /**
80 * Pass a new TetheringConfiguration instance each time when
81 * Tethering#updateConfiguration() is called.
82 */
83 public void updateConfiguration(TetheringConfiguration conf) {
84 mConfig = conf;
85 }
86
87 /**
88 * Tell EntitlementManager that a given type of tethering has been enabled
89 *
90 * @param type Tethering type
91 */
92 public void startTethering(int type) {
93 synchronized (mCurrentTethers) {
94 mCurrentTethers.add(type);
95 }
96 }
97
98 /**
99 * Tell EntitlementManager that a given type of tethering has been disabled
100 *
101 * @param type Tethering type
102 */
103 public void stopTethering(int type) {
104 synchronized (mCurrentTethers) {
105 mCurrentTethers.remove(type);
106 }
107 }
108
109 /**
110 * Check if the device requires a provisioning check in order to enable tethering.
111 *
112 * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
113 */
114 @VisibleForTesting
115 public boolean isTetherProvisioningRequired() {
116 if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
117 || mConfig.provisioningApp.length == 0) {
118 return false;
119 }
120 if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
121 return false;
122 }
123 return (mConfig.provisioningApp.length == 2);
124 }
125
126 /**
127 * Re-check tethering provisioning for enabled downstream tether types.
128 * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
129 */
130 public void reevaluateSimCardProvisioning() {
131 if (!mConfig.hasMobileHotspotProvisionApp()) return;
132 if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
133
134 final ArraySet<Integer> reevaluateType;
135 synchronized (mCurrentTethers) {
136 reevaluateType = new ArraySet<Integer>(mCurrentTethers);
137 }
138 for (Integer type : reevaluateType) {
139 startProvisionIntent(type);
140 }
141 }
142
143 // The logic here is aimed solely at confirming that a CarrierConfig exists
144 // and affirms that entitlement checks are not required.
145 //
146 // TODO: find a better way to express this, or alter the checking process
147 // entirely so that this is more intuitive.
148 private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
149 // Check carrier config for entitlement checks
150 final CarrierConfigManager configManager = (CarrierConfigManager) mContext
151 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
152 if (configManager == null) return false;
153
154 final PersistableBundle carrierConfig = configManager.getConfig();
155 if (carrierConfig == null) return false;
156
157 // A CarrierConfigManager was found and it has a config.
158 final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
159 CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
160 return !isEntitlementCheckRequired;
161 }
162
163 public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
164 Intent intent = new Intent();
165 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
166 intent.putExtra(EXTRA_RUN_PROVISION, true);
167 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
168 intent.setComponent(TETHER_SERVICE);
169 final long ident = Binder.clearCallingIdentity();
170 try {
171 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
172 } finally {
173 Binder.restoreCallingIdentity(ident);
174 }
175 }
176
177 public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
178 Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
179 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
180 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
181 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
182 final long ident = Binder.clearCallingIdentity();
183 try {
184 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
185 } finally {
186 Binder.restoreCallingIdentity(ident);
187 }
188 }
189
190 // Used by the SIM card change observation code.
191 // TODO: De-duplicate with above code, where possible.
192 private void startProvisionIntent(int tetherType) {
193 final Intent startProvIntent = new Intent();
194 startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
195 startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
196 startProvIntent.setComponent(TETHER_SERVICE);
197 mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
198 }
199
200 public void scheduleProvisioningRechecks(int type) {
201 Intent intent = new Intent();
202 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
203 intent.putExtra(EXTRA_SET_ALARM, true);
204 intent.setComponent(TETHER_SERVICE);
205 final long ident = Binder.clearCallingIdentity();
206 try {
207 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
208 } finally {
209 Binder.restoreCallingIdentity(ident);
210 }
211 }
212
213 public void cancelTetherProvisioningRechecks(int type) {
214 Intent intent = new Intent();
215 intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
216 intent.setComponent(TETHER_SERVICE);
217 final long ident = Binder.clearCallingIdentity();
218 try {
219 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
220 } finally {
221 Binder.restoreCallingIdentity(ident);
222 }
223 }
224}