1 /* 2 * Copyright (C) 2008 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.settings.wifi.tether; 18 19 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; 20 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; 21 import static android.net.TetheringConstants.EXTRA_REM_TETHER_TYPE; 22 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; 23 import static android.net.TetheringManager.TETHERING_BLUETOOTH; 24 import static android.net.TetheringManager.TETHERING_ETHERNET; 25 import static android.net.TetheringManager.TETHERING_INVALID; 26 import static android.net.TetheringManager.TETHERING_USB; 27 import static android.net.TetheringManager.TETHERING_WIFI; 28 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; 29 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; 30 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; 31 import static android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX; 32 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 33 34 import android.app.Activity; 35 import android.app.Service; 36 import android.app.usage.UsageStatsManager; 37 import android.content.BroadcastReceiver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.SharedPreferences; 42 import android.content.pm.PackageManager; 43 import android.content.pm.ResolveInfo; 44 import android.net.TetheringManager; 45 import android.os.IBinder; 46 import android.os.ResultReceiver; 47 import android.telephony.SubscriptionManager; 48 import android.text.TextUtils; 49 import android.util.ArrayMap; 50 import android.util.Log; 51 52 import androidx.annotation.VisibleForTesting; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Objects; 57 58 public class TetherService extends Service { 59 private static final String TAG = "TetherService"; 60 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61 62 @VisibleForTesting 63 public static final String EXTRA_RESULT = "EntitlementResult"; 64 @VisibleForTesting 65 public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; 66 @VisibleForTesting 67 public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = 68 "android.net.extra.TETHER_PROVISIONING_RESPONSE"; 69 @VisibleForTesting 70 public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = 71 "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; 72 73 // Activity results to match the activity provision protocol. 74 // Default to something not ok. 75 private static final int RESULT_DEFAULT = Activity.RESULT_CANCELED; 76 private static final int RESULT_OK = Activity.RESULT_OK; 77 78 private static final String TETHER_CHOICE = "TETHER_TYPE"; 79 private static final int MS_PER_HOUR = 60 * 60 * 1000; 80 81 private static final String PREFS = "tetherPrefs"; 82 private static final String KEY_TETHERS = "currentTethers"; 83 84 private int mCurrentTypeIndex; 85 private boolean mInProvisionCheck; 86 /** Intent action received from the provisioning app when entitlement check completes. */ 87 private String mExpectedProvisionResponseAction = null; 88 /** Intent action sent to the provisioning app to request an entitlement check. */ 89 private String mProvisionAction; 90 private int mSubId = INVALID_SUBSCRIPTION_ID; 91 private TetherServiceWrapper mWrapper; 92 private ArrayList<Integer> mCurrentTethers; 93 private ArrayMap<Integer, List<ResultReceiver>> mPendingCallbacks; 94 95 @Override onBind(Intent intent)96 public IBinder onBind(Intent intent) { 97 return null; 98 } 99 100 @Override onCreate()101 public void onCreate() { 102 super.onCreate(); 103 if (DEBUG) Log.d(TAG, "Creating TetherService"); 104 SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); 105 mCurrentTethers = stringToTethers(prefs.getString(KEY_TETHERS, "")); 106 mCurrentTypeIndex = 0; 107 mPendingCallbacks = new ArrayMap<>(3); 108 mPendingCallbacks.put(TETHERING_WIFI, new ArrayList<ResultReceiver>()); 109 mPendingCallbacks.put(TETHERING_USB, new ArrayList<ResultReceiver>()); 110 mPendingCallbacks.put(TETHERING_BLUETOOTH, new ArrayList<ResultReceiver>()); 111 mPendingCallbacks.put(TETHERING_ETHERNET, new ArrayList<ResultReceiver>()); 112 } 113 114 // Registers the broadcast receiver for the specified response action, first unregistering 115 // the receiver if it was registered for a different response action. maybeRegisterReceiver(final String responseAction)116 private void maybeRegisterReceiver(final String responseAction) { 117 if (Objects.equals(responseAction, mExpectedProvisionResponseAction)) return; 118 119 if (mExpectedProvisionResponseAction != null) unregisterReceiver(mReceiver); 120 121 registerReceiver(mReceiver, new IntentFilter(responseAction), 122 android.Manifest.permission.TETHER_PRIVILEGED, null /* handler */); 123 mExpectedProvisionResponseAction = responseAction; 124 if (DEBUG) Log.d(TAG, "registerReceiver " + responseAction); 125 } 126 stopSelfAndStartNotSticky()127 private int stopSelfAndStartNotSticky() { 128 stopSelf(); 129 return START_NOT_STICKY; 130 } 131 132 @Override onStartCommand(Intent intent, int flags, int startId)133 public int onStartCommand(Intent intent, int flags, int startId) { 134 if (intent.hasExtra(EXTRA_TETHER_SUBID)) { 135 final int tetherSubId = intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID); 136 final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId(); 137 if (tetherSubId != subId) { 138 Log.e(TAG, "This Provisioning request is outdated, current subId: " + subId); 139 if (!mInProvisionCheck) { 140 stopSelf(); 141 } 142 return START_NOT_STICKY; 143 } 144 mSubId = subId; 145 } 146 147 if (intent.hasExtra(EXTRA_ADD_TETHER_TYPE)) { 148 int type = intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID); 149 ResultReceiver callback = intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK); 150 if (callback != null) { 151 List<ResultReceiver> callbacksForType = mPendingCallbacks.get(type); 152 if (callbacksForType != null) { 153 callbacksForType.add(callback); 154 } else { 155 // Invalid tether type. Just ignore this request and report failure. 156 Log.e(TAG, "Invalid tethering type " + type + ", stopping"); 157 callback.send(TETHER_ERROR_UNKNOWN_IFACE, null); 158 return stopSelfAndStartNotSticky(); 159 } 160 } 161 162 if (!mCurrentTethers.contains(type)) { 163 if (DEBUG) Log.d(TAG, "Adding tether " + type); 164 mCurrentTethers.add(type); 165 } 166 } 167 168 mProvisionAction = intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION); 169 if (mProvisionAction == null) { 170 Log.e(TAG, "null provisioning action, stop "); 171 return stopSelfAndStartNotSticky(); 172 } 173 174 final String response = intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE); 175 if (response == null) { 176 Log.e(TAG, "null provisioning response, stop "); 177 return stopSelfAndStartNotSticky(); 178 } 179 maybeRegisterReceiver(response); 180 181 if (intent.hasExtra(EXTRA_REM_TETHER_TYPE)) { 182 if (!mInProvisionCheck) { 183 int type = intent.getIntExtra(EXTRA_REM_TETHER_TYPE, TETHERING_INVALID); 184 int index = mCurrentTethers.indexOf(type); 185 if (DEBUG) Log.d(TAG, "Removing tether " + type + ", index " + index); 186 if (index >= 0) { 187 removeTypeAtIndex(index); 188 } 189 } else { 190 if (DEBUG) Log.d(TAG, "Don't remove tether type during provisioning"); 191 } 192 } 193 194 if (intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)) { 195 startProvisioning(mCurrentTypeIndex); 196 } else if (!mInProvisionCheck) { 197 // If we aren't running any provisioning, no reason to stay alive. 198 if (DEBUG) Log.d(TAG, "Stopping self. startid: " + startId); 199 return stopSelfAndStartNotSticky(); 200 } 201 // We want to be started if we are killed accidently, so that we can be sure we finish 202 // the check. 203 return START_REDELIVER_INTENT; 204 } 205 206 @Override onDestroy()207 public void onDestroy() { 208 if (mInProvisionCheck) { 209 Log.e(TAG, "TetherService getting destroyed while mid-provisioning" 210 + mCurrentTethers.get(mCurrentTypeIndex)); 211 } 212 SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); 213 prefs.edit().putString(KEY_TETHERS, tethersToString(mCurrentTethers)).commit(); 214 215 if (mExpectedProvisionResponseAction != null) { 216 unregisterReceiver(mReceiver); 217 mExpectedProvisionResponseAction = null; 218 } 219 if (DEBUG) Log.d(TAG, "Destroying TetherService"); 220 super.onDestroy(); 221 } 222 removeTypeAtIndex(int index)223 private void removeTypeAtIndex(int index) { 224 mCurrentTethers.remove(index); 225 // If we are currently in the middle of a check, we may need to adjust the 226 // index accordingly. 227 if (DEBUG) Log.d(TAG, "mCurrentTypeIndex: " + mCurrentTypeIndex); 228 if (index <= mCurrentTypeIndex && mCurrentTypeIndex > 0) { 229 mCurrentTypeIndex--; 230 } 231 } 232 stringToTethers(String tethersStr)233 private ArrayList<Integer> stringToTethers(String tethersStr) { 234 ArrayList<Integer> ret = new ArrayList<Integer>(); 235 if (TextUtils.isEmpty(tethersStr)) return ret; 236 237 String[] tethersSplit = tethersStr.split(","); 238 for (int i = 0; i < tethersSplit.length; i++) { 239 ret.add(Integer.parseInt(tethersSplit[i])); 240 } 241 return ret; 242 } 243 tethersToString(ArrayList<Integer> tethers)244 private String tethersToString(ArrayList<Integer> tethers) { 245 final StringBuffer buffer = new StringBuffer(); 246 final int N = tethers.size(); 247 for (int i = 0; i < N; i++) { 248 if (i != 0) { 249 buffer.append(','); 250 } 251 buffer.append(tethers.get(i)); 252 } 253 254 return buffer.toString(); 255 } 256 disableTethering(final int tetheringType)257 private void disableTethering(final int tetheringType) { 258 final TetheringManager tm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE); 259 tm.stopTethering(tetheringType); 260 } 261 startProvisioning(int index)262 private void startProvisioning(int index) { 263 if (index >= mCurrentTethers.size()) return; 264 265 Intent intent = getProvisionBroadcastIntent(index); 266 setEntitlementAppActive(index); 267 268 if (DEBUG) { 269 Log.d(TAG, "Sending provisioning broadcast: " + intent.getAction() 270 + " type: " + mCurrentTethers.get(index)); 271 } 272 273 sendBroadcast(intent); 274 mInProvisionCheck = true; 275 } 276 getProvisionBroadcastIntent(int index)277 private Intent getProvisionBroadcastIntent(int index) { 278 if (mProvisionAction == null) Log.wtf(TAG, "null provisioning action"); 279 Intent intent = new Intent(mProvisionAction); 280 int type = mCurrentTethers.get(index); 281 intent.putExtra(TETHER_CHOICE, type); 282 intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, mSubId); 283 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND 284 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 285 286 return intent; 287 } 288 setEntitlementAppActive(int index)289 private void setEntitlementAppActive(int index) { 290 final PackageManager packageManager = getPackageManager(); 291 Intent intent = getProvisionBroadcastIntent(index); 292 List<ResolveInfo> resolvers = 293 packageManager.queryBroadcastReceivers(intent, PackageManager.MATCH_ALL); 294 if (resolvers.isEmpty()) { 295 Log.e(TAG, "No found BroadcastReceivers for provision intent."); 296 return; 297 } 298 299 for (ResolveInfo resolver : resolvers) { 300 if (resolver.activityInfo.applicationInfo.isSystemApp()) { 301 String packageName = resolver.activityInfo.packageName; 302 getTetherServiceWrapper().setAppInactive(packageName, false); 303 } 304 } 305 } 306 fireCallbacksForType(int type, int result)307 private void fireCallbacksForType(int type, int result) { 308 List<ResultReceiver> callbacksForType = mPendingCallbacks.get(type); 309 if (callbacksForType == null) { 310 return; 311 } 312 int errorCode = result == RESULT_OK ? TETHER_ERROR_NO_ERROR : 313 TETHER_ERROR_PROVISIONING_FAILED; 314 for (ResultReceiver callback : callbacksForType) { 315 if (DEBUG) Log.d(TAG, "Firing result: " + errorCode + " to callback"); 316 callback.send(errorCode, null); 317 } 318 callbacksForType.clear(); 319 } 320 321 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 322 @Override 323 public void onReceive(Context context, Intent intent) { 324 if (DEBUG) Log.d(TAG, "Got provision result " + intent); 325 326 if (!intent.getAction().equals(mExpectedProvisionResponseAction)) { 327 Log.e(TAG, "Received provisioning response for unexpected action=" 328 + intent.getAction() + ", expected=" + mExpectedProvisionResponseAction); 329 return; 330 } 331 332 if (!mInProvisionCheck) { 333 Log.e(TAG, "Unexpected provisioning response when not in provisioning check" 334 + intent); 335 return; 336 } 337 int checkType = mCurrentTethers.get(mCurrentTypeIndex); 338 mInProvisionCheck = false; 339 int result = intent.getIntExtra(EXTRA_RESULT, RESULT_DEFAULT); 340 if (result != RESULT_OK) disableTethering(checkType); 341 fireCallbacksForType(checkType, result); 342 343 if (++mCurrentTypeIndex >= mCurrentTethers.size()) { 344 // We are done with all checks, time to die. 345 stopSelf(); 346 } else { 347 // Start the next check in our list. 348 startProvisioning(mCurrentTypeIndex); 349 } 350 } 351 }; 352 353 @VisibleForTesting setTetherServiceWrapper(TetherServiceWrapper wrapper)354 void setTetherServiceWrapper(TetherServiceWrapper wrapper) { 355 mWrapper = wrapper; 356 } 357 getTetherServiceWrapper()358 private TetherServiceWrapper getTetherServiceWrapper() { 359 if (mWrapper == null) { 360 mWrapper = new TetherServiceWrapper(this); 361 } 362 return mWrapper; 363 } 364 365 /** 366 * A static helper class used for tests. UsageStatsManager cannot be mocked out because 367 * it's marked final. This class can be mocked out instead. 368 */ 369 @VisibleForTesting 370 public static class TetherServiceWrapper { 371 private final UsageStatsManager mUsageStatsManager; 372 TetherServiceWrapper(Context context)373 TetherServiceWrapper(Context context) { 374 mUsageStatsManager = (UsageStatsManager) 375 context.getSystemService(Context.USAGE_STATS_SERVICE); 376 } 377 setAppInactive(String packageName, boolean isInactive)378 void setAppInactive(String packageName, boolean isInactive) { 379 mUsageStatsManager.setAppInactive(packageName, isInactive); 380 } 381 getActiveDataSubscriptionId()382 int getActiveDataSubscriptionId() { 383 return SubscriptionManager.getActiveDataSubscriptionId(); 384 } 385 } 386 } 387