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