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