• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ons;
18 
19 import android.app.Service;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.SharedPreferences;
25 import android.content.pm.PackageManager;
26 import android.os.Binder;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.telephony.AvailableNetworkInfo;
33 import android.telephony.Rlog;
34 import android.telephony.SubscriptionInfo;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.TelephonyManager;
37 
38 import com.android.internal.annotations.VisibleForTesting;
39 import com.android.internal.telephony.IOns;
40 import com.android.internal.telephony.ISetOpportunisticDataCallback;
41 import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
42 import com.android.internal.telephony.TelephonyIntents;
43 import com.android.internal.telephony.TelephonyPermissions;
44 
45 import java.util.ArrayList;
46 import java.util.HashMap;
47 import java.util.List;
48 
49 /**
50  * OpportunisticNetworkService implements ions.
51  * It scans network and matches the results with opportunistic subscriptions.
52  * Use the same to provide user opportunistic data in areas with corresponding networks
53  */
54 public class OpportunisticNetworkService extends Service {
55     private Context mContext;
56     private TelephonyManager mTelephonyManager;
57     private SubscriptionManager mSubscriptionManager;
58 
59     private final Object mLock = new Object();
60     private boolean mIsEnabled;
61     private ONSProfileSelector mProfileSelector;
62     private SharedPreferences mSharedPref;
63     private HashMap<String, ONSConfigInput> mONSConfigInputHashMap;
64 
65     private static final String TAG = "ONS";
66     private static final String PREF_NAME = TAG;
67     private static final String PREF_ENABLED = "isEnabled";
68     private static final String SERVICE_NAME = "ions";
69     private static final String CARRIER_APP_CONFIG_NAME = "carrierApp";
70     private static final String SYSTEM_APP_CONFIG_NAME = "systemApp";
71     private static final boolean DBG = true;
72     /* message to indicate sim state update */
73     private static final int MSG_SIM_STATE_CHANGE = 1;
74 
75     /**
76      * Profile selection callback. Will be called once Profile selector decides on
77      * the opportunistic data profile.
78      */
79     private ONSProfileSelector.ONSProfileSelectionCallback  mProfileSelectionCallback =
80             new ONSProfileSelector.ONSProfileSelectionCallback() {
81 
82                 @Override
83                 public void onProfileSelectionDone() {
84                     logDebug("profile selection done");
85                 }
86             };
87 
88     /** Broadcast receiver to get SIM card state changed event */
89     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
90         @Override
91         public void onReceive(Context context, Intent intent) {
92             mHandler.sendEmptyMessage(MSG_SIM_STATE_CHANGE);
93         }
94     };
95 
96     private Handler mHandler = new Handler() {
97         @Override
98         public void handleMessage(Message msg) {
99             switch (msg.what) {
100                 case MSG_SIM_STATE_CHANGE:
101                     synchronized (mLock) {
102                         handleSimStateChange();
103                     }
104                     break;
105                 default:
106                     log("invalid message");
107                     break;
108             }
109         }
110     };
111 
enforceModifyPhoneStatePermission(Context context)112     private static boolean enforceModifyPhoneStatePermission(Context context) {
113         if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
114                 == PackageManager.PERMISSION_GRANTED) {
115             return true;
116         }
117 
118         return false;
119     }
120 
handleSimStateChange()121     private void handleSimStateChange() {
122         logDebug("SIM state changed");
123         ONSConfigInput onsConfigInput = mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME);
124         if (onsConfigInput == null) {
125             return;
126         }
127         List<SubscriptionInfo> subscriptionInfos =
128             mSubscriptionManager.getActiveSubscriptionInfoList(false);
129         for (SubscriptionInfo subscriptionInfo : subscriptionInfos) {
130             if (subscriptionInfo.getSubscriptionId() == onsConfigInput.getPrimarySub()) {
131                 return;
132             }
133         }
134 
135         logDebug("Carrier subscription is not available, removing entry");
136         mONSConfigInputHashMap.put(CARRIER_APP_CONFIG_NAME, null);
137         if (!mIsEnabled) {
138             return;
139         }
140         if (mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME) != null) {
141             mProfileSelector.startProfileSelection(
142                     mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME).getAvailableNetworkInfos(),
143                     mONSConfigInputHashMap.get(
144                             SYSTEM_APP_CONFIG_NAME).getAvailableNetworkCallback());
145         } else {
146             mProfileSelector.stopProfileSelection(mONSConfigInputHashMap.get(
147                     SYSTEM_APP_CONFIG_NAME).getAvailableNetworkCallback());
148         }
149     }
150 
hasOpportunisticSubPrivilege(String callingPackage, int subId)151     private boolean hasOpportunisticSubPrivilege(String callingPackage, int subId) {
152         return mTelephonyManager.hasCarrierPrivileges(subId)
153                 || mSubscriptionManager.canManageSubscription(
154                 mProfileSelector.getOpprotunisticSubInfo(subId), callingPackage);
155     }
156 
157     private final IOns.Stub mBinder = new IOns.Stub() {
158         /**
159          * Enable or disable Opportunistic Network service.
160          *
161          * This method should be called to enable or disable
162          * OpportunisticNetwork service on the device.
163          *
164          * <p>
165          * Requires Permission:
166          *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
167          * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
168          *
169          * @param enable enable(True) or disable(False)
170          * @param callingPackage caller's package name
171          * @return returns true if successfully set.
172          */
173         @Override
174         public boolean setEnable(boolean enable, String callingPackage) {
175             TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
176                     mContext, mSubscriptionManager.getDefaultSubscriptionId(), "setEnable");
177             log("setEnable: " + enable);
178 
179             final long identity = Binder.clearCallingIdentity();
180             try {
181                 enableOpportunisticNetwork(enable);
182             } finally {
183                 Binder.restoreCallingIdentity(identity);
184             }
185 
186             return true;
187         }
188 
189         /**
190          * is Opportunistic Network service enabled
191          *
192          * This method should be called to determine if the Opportunistic Network service
193          * is enabled
194          *
195          * <p>
196          * Requires Permission:
197          *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
198          * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
199          *
200          * @param callingPackage caller's package name
201          */
202         @Override
203         public boolean isEnabled(String callingPackage) {
204             TelephonyPermissions
205                     .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
206                             mContext, mSubscriptionManager.getDefaultSubscriptionId(), "isEnabled");
207             return mIsEnabled;
208         }
209 
210         /**
211          * Set preferred opportunistic data.
212          *
213          * <p>Requires that the calling app has carrier privileges on both primary and
214          * secondary subscriptions (see
215          * {@link #hasCarrierPrivileges}), or has permission
216          * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
217          * @param subId which opportunistic subscription
218          * {@link SubscriptionManager#getOpportunisticSubscriptions} is preferred for cellular data.
219          * Pass {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} to unset the preference
220          * @param needValidation whether validation is needed before switch happens.
221          * @param callback callback upon request completion.
222          * @param callingPackage caller's package name
223          *
224          */
225         public void setPreferredDataSubscriptionId(int subId, boolean needValidation,
226                 ISetOpportunisticDataCallback callbackStub, String callingPackage) {
227             logDebug("setPreferredDataSubscriptionId subId:" + subId + "callingPackage: " + callingPackage);
228             if (!enforceModifyPhoneStatePermission(mContext)) {
229                 TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
230                         mSubscriptionManager.getDefaultSubscriptionId(), "setPreferredDataSubscriptionId");
231                 if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
232                     TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId,
233                             "setPreferredDataSubscriptionId");
234                 }
235             }
236             final long identity = Binder.clearCallingIdentity();
237             try {
238                 mProfileSelector.selectProfileForData(subId, needValidation, callbackStub);
239             } finally {
240                 Binder.restoreCallingIdentity(identity);
241             }
242         }
243 
244         /**
245          * Get preferred default data sub Id
246          *
247          * <p>Requires that the calling app has carrier privileges
248          * (see {@link #hasCarrierPrivileges}),or has either
249          * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or.
250          * {@link android.Manifest.permission#READ_PHONE_STATE} permission.
251          * @return subId preferred opportunistic subscription id or
252          * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
253          * subscription id
254          *
255          */
256         public int getPreferredDataSubscriptionId(String callingPackage) {
257             TelephonyPermissions
258                     .checkCallingOrSelfReadPhoneState(mContext,
259                             mSubscriptionManager.getDefaultSubscriptionId(),
260                             callingPackage, "getPreferredDataSubscriptionId");
261             final long identity = Binder.clearCallingIdentity();
262             try {
263                 return mProfileSelector.getPreferredDataSubscriptionId();
264             } finally {
265                 Binder.restoreCallingIdentity(identity);
266             }
267         }
268 
269         /**
270          * Update availability of a list of networks in the current location.
271          *
272          * This api should be called if the caller is aware of the availability of a network
273          * at the current location. This information will be used by OpportunisticNetwork service
274          * to decide to attach to the network. If an empty list is passed,
275          * it is assumed that no network is available.
276          *  @param availableNetworks is a list of available network information.
277          *  @param callback callback upon request completion.
278          *  @param callingPackage caller's package name
279          * <p>
280          * <p>Requires that the calling app has carrier privileges on both primary and
281          * secondary subscriptions (see
282          * {@link #hasCarrierPrivileges}), or has permission
283          * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
284          *
285          */
286         public void updateAvailableNetworks(List<AvailableNetworkInfo> availableNetworks,
287                 IUpdateAvailableNetworksCallback callbackStub, String callingPackage) {
288             logDebug("updateAvailableNetworks: " + availableNetworks);
289             /* check if system app */
290             if (enforceModifyPhoneStatePermission(mContext)) {
291                 handleSystemAppAvailableNetworks(
292                         (ArrayList<AvailableNetworkInfo>) availableNetworks, callbackStub);
293             } else {
294                 /* check if the app has primary carrier permission */
295                 TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
296                         mSubscriptionManager.getDefaultSubscriptionId(), "updateAvailableNetworks");
297                 handleCarrierAppAvailableNetworks(
298                         (ArrayList<AvailableNetworkInfo>) availableNetworks, callbackStub,
299                         callingPackage);
300             }
301         }
302     };
303 
304     @Override
onBind(Intent intent)305     public IBinder onBind(Intent intent) {
306         return mBinder;
307     }
308 
309     @Override
onCreate()310     public void onCreate() {
311         initialize(getBaseContext());
312 
313         /* register the service */
314         if (ServiceManager.getService(SERVICE_NAME) == null) {
315             ServiceManager.addService(SERVICE_NAME, mBinder);
316         }
317     }
318 
319     @Override
onStartCommand(Intent intent, int flags, int startId)320     public int onStartCommand(Intent intent, int flags, int startId) {
321         if (intent == null) {
322             return START_STICKY;
323         }
324 
325         String action = intent.getAction();
326         if (action == null) {
327             return START_STICKY;
328         }
329 
330         switch (action) {
331             case ONSProfileSelector.ACTION_SUB_SWITCH: {
332                 if (mProfileSelector != null) {
333                     mProfileSelector.onSubSwitchComplete(intent);
334                 }
335                 break;
336             }
337         }
338 
339         return START_STICKY;
340     }
341 
342     @Override
onDestroy()343     public void onDestroy() {
344         super.onDestroy();
345         log("Destroyed Successfully...");
346 
347     }
348 
349     /**
350      * initialize ONS and register as service.
351      * Read persistent state to update enable state
352      * Start sub components if already enabled.
353      * @param context context instance
354      */
initialize(Context context)355     private void initialize(Context context) {
356         mContext = context;
357         mTelephonyManager = TelephonyManager.from(mContext);
358         mProfileSelector = new ONSProfileSelector(mContext, mProfileSelectionCallback);
359         mSharedPref = mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
360         mSubscriptionManager = (SubscriptionManager) mContext.getSystemService(
361                 Context.TELEPHONY_SUBSCRIPTION_SERVICE);
362         mONSConfigInputHashMap = new HashMap<String, ONSConfigInput>();
363         mContext.registerReceiver(mBroadcastReceiver,
364             new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
365         enableOpportunisticNetwork(getPersistentEnableState());
366     }
367 
handleCarrierAppAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub, String callingPackage)368     private void handleCarrierAppAvailableNetworks(
369             ArrayList<AvailableNetworkInfo> availableNetworks,
370             IUpdateAvailableNetworksCallback callbackStub, String callingPackage) {
371         if ((availableNetworks != null) && (availableNetworks.size() > 0)) {
372             /* carrier apps should report only subscription */
373             if (availableNetworks.size() > 1) {
374                 log("Carrier app should not pass more than one subscription");
375                 sendUpdateNetworksCallbackHelper(callbackStub,
376                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
377                 return;
378             }
379 
380             if (!mProfileSelector.hasOpprotunisticSub(availableNetworks)) {
381                 log("No opportunistic subscriptions received");
382                 sendUpdateNetworksCallbackHelper(callbackStub,
383                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
384                 return;
385             }
386 
387             for (AvailableNetworkInfo availableNetworkInfo : availableNetworks) {
388                 if (mSubscriptionManager.isActiveSubId(availableNetworkInfo.getSubId())) {
389                     TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
390                         availableNetworkInfo.getSubId(), "updateAvailableNetworks");
391                 } else {
392                     /* check if the app has opportunistic carrier permission */
393                     if (!hasOpportunisticSubPrivilege(callingPackage,
394                         availableNetworkInfo.getSubId())) {
395                         log("No carrier privilege for opportunistic subscription");
396                         sendUpdateNetworksCallbackHelper(callbackStub,
397                             TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_CARRIER_PRIVILEGE);
398                         return;
399                     }
400                 }
401             }
402 
403             final long identity = Binder.clearCallingIdentity();
404             try {
405                 ONSConfigInput onsConfigInput = new ONSConfigInput(availableNetworks, callbackStub);
406                 onsConfigInput.setPrimarySub(
407                         mSubscriptionManager.getDefaultVoiceSubscriptionInfo().getSubscriptionId());
408                 onsConfigInput.setPreferredDataSub(availableNetworks.get(0).getSubId());
409                 mONSConfigInputHashMap.put(CARRIER_APP_CONFIG_NAME, onsConfigInput);
410 
411                 if (mIsEnabled) {
412                     /*  if carrier is reporting availability, then it takes higher priority. */
413                     mProfileSelector.startProfileSelection(availableNetworks, callbackStub);
414                 }
415             } finally {
416                 Binder.restoreCallingIdentity(identity);
417             }
418         } else {
419             final long identity = Binder.clearCallingIdentity();
420             try {
421                 mONSConfigInputHashMap.put(CARRIER_APP_CONFIG_NAME, null);
422                 if (!mIsEnabled) {
423                     sendUpdateNetworksCallbackHelper(callbackStub,
424                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
425                     return;
426                 }
427                 /* if carrier is reporting unavailability, then decide whether to start
428                    system app request or not. */
429                 if (mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME) != null) {
430                     sendUpdateNetworksCallbackHelper(callbackStub,
431                             TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
432                     mProfileSelector.startProfileSelection(
433                             mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME)
434                                     .getAvailableNetworkInfos(),
435                             mONSConfigInputHashMap.get(
436                                     SYSTEM_APP_CONFIG_NAME).getAvailableNetworkCallback());
437                 } else {
438                     mProfileSelector.stopProfileSelection(callbackStub);
439                 }
440             } finally {
441                 Binder.restoreCallingIdentity(identity);
442             }
443         }
444     }
445 
sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result)446     private void sendUpdateNetworksCallbackHelper(IUpdateAvailableNetworksCallback callback, int result) {
447         if (callback == null) return;
448         try {
449             callback.onComplete(result);
450         } catch (RemoteException exception) {
451             log("RemoteException " + exception);
452         }
453     }
454 
handleSystemAppAvailableNetworks( ArrayList<AvailableNetworkInfo> availableNetworks, IUpdateAvailableNetworksCallback callbackStub)455     private void handleSystemAppAvailableNetworks(
456             ArrayList<AvailableNetworkInfo> availableNetworks,
457             IUpdateAvailableNetworksCallback callbackStub) {
458         final long identity = Binder.clearCallingIdentity();
459         try {
460             if ((availableNetworks != null) && (availableNetworks.size() > 0)) {
461                 /* all subscriptions should be opportunistic subscriptions */
462                 if (!mProfileSelector.hasOpprotunisticSub(availableNetworks)) {
463                     log("No opportunistic subscriptions received");
464                     sendUpdateNetworksCallbackHelper(callbackStub,
465                             TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
466                     return;
467                 }
468                 mONSConfigInputHashMap.put(SYSTEM_APP_CONFIG_NAME,
469                         new ONSConfigInput(availableNetworks, callbackStub));
470 
471                 /* reporting availability. proceed if carrier app has not requested any */
472                 if (mIsEnabled && mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) == null) {
473                     mProfileSelector.startProfileSelection(availableNetworks, callbackStub);
474                 }
475             } else {
476                 if (!mIsEnabled) {
477                     mONSConfigInputHashMap.put(SYSTEM_APP_CONFIG_NAME, null);
478                     sendUpdateNetworksCallbackHelper(callbackStub,
479                         TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
480                     return;
481                 }
482                 /* reporting unavailability */
483                 mONSConfigInputHashMap.put(SYSTEM_APP_CONFIG_NAME, null);
484                 if (mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) == null) {
485                     mProfileSelector.stopProfileSelection(callbackStub);
486                 }
487             }
488         } finally {
489             Binder.restoreCallingIdentity(identity);
490         }
491     }
492 
getPersistentEnableState()493     private boolean getPersistentEnableState() {
494         return mSharedPref.getBoolean(PREF_ENABLED, true);
495     }
496 
updateEnableState(boolean enable)497     private void updateEnableState(boolean enable) {
498         mIsEnabled = enable;
499         mSharedPref.edit().putBoolean(PREF_ENABLED, mIsEnabled).apply();
500     }
501 
502     /**
503      * update the enable state
504      * start profile selection if enabled.
505      * @param enable enable(true) or disable(false)
506      */
enableOpportunisticNetwork(boolean enable)507     private void enableOpportunisticNetwork(boolean enable) {
508         synchronized (mLock) {
509             if (mIsEnabled != enable) {
510                 updateEnableState(enable);
511                 if (!mIsEnabled) {
512                     mProfileSelector.stopProfileSelection(null);
513                 } else {
514                     if (mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME) != null &&
515                         mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME)
516                             .getAvailableNetworkInfos() != null) {
517                         mProfileSelector.startProfileSelection(
518                             mONSConfigInputHashMap.get(CARRIER_APP_CONFIG_NAME)
519                                 .getAvailableNetworkInfos(),
520                             mONSConfigInputHashMap.get(
521                                 CARRIER_APP_CONFIG_NAME).getAvailableNetworkCallback());
522                     } else if (mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME) != null &&
523                         mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME)
524                             .getAvailableNetworkInfos() != null) {
525                         mProfileSelector.startProfileSelection(
526                             mONSConfigInputHashMap.get(SYSTEM_APP_CONFIG_NAME)
527                                 .getAvailableNetworkInfos(),
528                             mONSConfigInputHashMap.get(
529                                 SYSTEM_APP_CONFIG_NAME).getAvailableNetworkCallback());
530                     }
531                 }
532             }
533         }
534         logDebug("service is enable state " + mIsEnabled);
535     }
536 
log(String msg)537     private void log(String msg) {
538         Rlog.d(TAG, msg);
539     }
540 
logDebug(String msg)541     private void logDebug(String msg) {
542         if (DBG) Rlog.d(TAG, msg);
543     }
544 }
545