blob: 70ab389834460d80cee089490382ae52415a3f86 [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;
markchienf2731272019-01-16 17:44:13 +080024import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
25import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
26import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
markchienb6eb2c22018-07-18 14:29:20 +080027
28import static com.android.internal.R.string.config_wifi_tether_enable;
29
30import android.annotation.Nullable;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.res.Resources;
35import android.net.util.SharedLog;
36import android.os.Binder;
markchienf2731272019-01-16 17:44:13 +080037import android.os.Bundle;
38import android.os.Handler;
39import android.os.Parcel;
markchienb6eb2c22018-07-18 14:29:20 +080040import android.os.PersistableBundle;
41import android.os.ResultReceiver;
42import android.os.UserHandle;
43import android.provider.Settings;
44import android.telephony.CarrierConfigManager;
45import android.util.ArraySet;
markchienf2731272019-01-16 17:44:13 +080046import android.util.Log;
47import android.util.SparseIntArray;
markchienb6eb2c22018-07-18 14:29:20 +080048
49import com.android.internal.annotations.GuardedBy;
50import com.android.internal.annotations.VisibleForTesting;
markchienf2731272019-01-16 17:44:13 +080051import com.android.internal.util.StateMachine;
markchienb6eb2c22018-07-18 14:29:20 +080052import com.android.server.connectivity.MockableSystemProperties;
53
54/**
55 * This class encapsulates entitlement/provisioning mechanics
56 * provisioning check only applies to the use of the mobile network as an upstream
57 *
58 * @hide
59 */
60public class EntitlementManager {
61 private static final String TAG = EntitlementManager.class.getSimpleName();
markchienf2731272019-01-16 17:44:13 +080062 private static final boolean DBG = false;
markchienb6eb2c22018-07-18 14:29:20 +080063
64 // {@link ComponentName} of the Service used to run tether provisioning.
65 private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
66 Resources.getSystem().getString(config_wifi_tether_enable));
67 protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
68
69 // The ArraySet contains enabled downstream types, ex:
70 // {@link ConnectivityManager.TETHERING_WIFI}
71 // {@link ConnectivityManager.TETHERING_USB}
72 // {@link ConnectivityManager.TETHERING_BLUETOOTH}
73 @GuardedBy("mCurrentTethers")
74 private final ArraySet<Integer> mCurrentTethers;
75 private final Context mContext;
76 private final MockableSystemProperties mSystemProperties;
77 private final SharedLog mLog;
markchienf2731272019-01-16 17:44:13 +080078 private final Handler mMasterHandler;
79 private final SparseIntArray mEntitlementCacheValue;
markchienb6eb2c22018-07-18 14:29:20 +080080 @Nullable
81 private TetheringConfiguration mConfig;
82
markchienf2731272019-01-16 17:44:13 +080083 public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
markchienb6eb2c22018-07-18 14:29:20 +080084 MockableSystemProperties systemProperties) {
85 mContext = ctx;
markchienf2731272019-01-16 17:44:13 +080086 mLog = log.forSubComponent(TAG);
markchienb6eb2c22018-07-18 14:29:20 +080087 mCurrentTethers = new ArraySet<Integer>();
88 mSystemProperties = systemProperties;
markchienf2731272019-01-16 17:44:13 +080089 mEntitlementCacheValue = new SparseIntArray();
90 mMasterHandler = tetherMasterSM.getHandler();
markchienb6eb2c22018-07-18 14:29:20 +080091 }
92
93 /**
94 * Pass a new TetheringConfiguration instance each time when
95 * Tethering#updateConfiguration() is called.
96 */
97 public void updateConfiguration(TetheringConfiguration conf) {
98 mConfig = conf;
99 }
100
101 /**
102 * Tell EntitlementManager that a given type of tethering has been enabled
103 *
104 * @param type Tethering type
105 */
106 public void startTethering(int type) {
107 synchronized (mCurrentTethers) {
108 mCurrentTethers.add(type);
109 }
110 }
111
112 /**
113 * Tell EntitlementManager that a given type of tethering has been disabled
114 *
115 * @param type Tethering type
116 */
117 public void stopTethering(int type) {
118 synchronized (mCurrentTethers) {
119 mCurrentTethers.remove(type);
120 }
121 }
122
123 /**
124 * Check if the device requires a provisioning check in order to enable tethering.
125 *
126 * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
127 */
128 @VisibleForTesting
129 public boolean isTetherProvisioningRequired() {
130 if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
131 || mConfig.provisioningApp.length == 0) {
132 return false;
133 }
134 if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
135 return false;
136 }
137 return (mConfig.provisioningApp.length == 2);
138 }
139
140 /**
141 * Re-check tethering provisioning for enabled downstream tether types.
142 * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
143 */
144 public void reevaluateSimCardProvisioning() {
markchienf2731272019-01-16 17:44:13 +0800145 synchronized (mEntitlementCacheValue) {
146 mEntitlementCacheValue.clear();
147 }
148
markchienb6eb2c22018-07-18 14:29:20 +0800149 if (!mConfig.hasMobileHotspotProvisionApp()) return;
150 if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
151
152 final ArraySet<Integer> reevaluateType;
153 synchronized (mCurrentTethers) {
154 reevaluateType = new ArraySet<Integer>(mCurrentTethers);
155 }
156 for (Integer type : reevaluateType) {
157 startProvisionIntent(type);
158 }
159 }
160
markchien29a650c2019-03-19 20:57:04 +0800161 /** Get carrier configuration bundle. */
162 public PersistableBundle getCarrierConfig() {
163 final CarrierConfigManager configManager = (CarrierConfigManager) mContext
164 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
165 if (configManager == null) return null;
166
167 final PersistableBundle carrierConfig = configManager.getConfig();
168
169 if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
170 return carrierConfig;
171 }
172
173 return null;
174 }
175
markchienb6eb2c22018-07-18 14:29:20 +0800176 // The logic here is aimed solely at confirming that a CarrierConfig exists
177 // and affirms that entitlement checks are not required.
178 //
179 // TODO: find a better way to express this, or alter the checking process
180 // entirely so that this is more intuitive.
181 private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
182 // Check carrier config for entitlement checks
markchien29a650c2019-03-19 20:57:04 +0800183 final PersistableBundle carrierConfig = getCarrierConfig();
markchienb6eb2c22018-07-18 14:29:20 +0800184 if (carrierConfig == null) return false;
185
186 // A CarrierConfigManager was found and it has a config.
187 final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
188 CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
189 return !isEntitlementCheckRequired;
190 }
191
192 public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
193 Intent intent = new Intent();
194 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
195 intent.putExtra(EXTRA_RUN_PROVISION, true);
196 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
197 intent.setComponent(TETHER_SERVICE);
198 final long ident = Binder.clearCallingIdentity();
199 try {
200 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
201 } finally {
202 Binder.restoreCallingIdentity(ident);
203 }
204 }
205
206 public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
markchienf2731272019-01-16 17:44:13 +0800207 runUiTetherProvisioning(type, receiver);
208 }
209
210 @VisibleForTesting
211 protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
markchienb6eb2c22018-07-18 14:29:20 +0800212 Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
213 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
214 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
215 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
216 final long ident = Binder.clearCallingIdentity();
217 try {
218 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
219 } finally {
220 Binder.restoreCallingIdentity(ident);
221 }
222 }
223
224 // Used by the SIM card change observation code.
225 // TODO: De-duplicate with above code, where possible.
226 private void startProvisionIntent(int tetherType) {
227 final Intent startProvIntent = new Intent();
228 startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
229 startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
230 startProvIntent.setComponent(TETHER_SERVICE);
231 mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
232 }
233
234 public void scheduleProvisioningRechecks(int type) {
235 Intent intent = new Intent();
236 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
237 intent.putExtra(EXTRA_SET_ALARM, true);
238 intent.setComponent(TETHER_SERVICE);
239 final long ident = Binder.clearCallingIdentity();
240 try {
241 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
242 } finally {
243 Binder.restoreCallingIdentity(ident);
244 }
245 }
246
247 public void cancelTetherProvisioningRechecks(int type) {
248 Intent intent = new Intent();
249 intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
250 intent.setComponent(TETHER_SERVICE);
251 final long ident = Binder.clearCallingIdentity();
252 try {
253 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
254 } finally {
255 Binder.restoreCallingIdentity(ident);
256 }
257 }
markchienf2731272019-01-16 17:44:13 +0800258
259 private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
260 ResultReceiver rr = new ResultReceiver(mMasterHandler) {
261 @Override
262 protected void onReceiveResult(int resultCode, Bundle resultData) {
263 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
264 receiver.send(updatedCacheValue, null);
265 }
266 };
267
268 return writeToParcel(rr);
269 }
270
271 private ResultReceiver writeToParcel(final ResultReceiver receiver) {
272 // This is necessary to avoid unmarshalling issues when sending the receiver
273 // across processes.
274 Parcel parcel = Parcel.obtain();
275 receiver.writeToParcel(parcel, 0);
276 parcel.setDataPosition(0);
277 ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
278 parcel.recycle();
279 return receiverForSending;
280 }
281
282 /**
283 * Update the last entitlement value to internal cache
284 *
285 * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
286 * @param resultCode last entitlement value
287 * @return the last updated entitlement value
288 */
289 public int updateEntitlementCacheValue(int type, int resultCode) {
290 if (DBG) {
291 Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
292 }
293 synchronized (mEntitlementCacheValue) {
294 if (resultCode == TETHER_ERROR_NO_ERROR) {
295 mEntitlementCacheValue.put(type, resultCode);
296 return resultCode;
297 } else {
298 mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
299 return TETHER_ERROR_PROVISION_FAILED;
300 }
301 }
302 }
303
304 /** Get the last value of the tethering entitlement check. */
markchien9554abf2019-03-06 16:25:00 +0800305 public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
markchienf2731272019-01-16 17:44:13 +0800306 boolean showEntitlementUi) {
307 if (!isTetherProvisioningRequired()) {
308 receiver.send(TETHER_ERROR_NO_ERROR, null);
309 return;
310 }
311
312 final int cacheValue;
313 synchronized (mEntitlementCacheValue) {
314 cacheValue = mEntitlementCacheValue.get(
315 downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
316 }
317 if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
318 receiver.send(cacheValue, null);
319 } else {
320 ResultReceiver proxy = buildProxyReceiver(downstream, receiver);
321 runUiTetherProvisioning(downstream, proxy);
322 }
323 }
markchienb6eb2c22018-07-18 14:29:20 +0800324}