1 /* 2 * Copyright (C) 2015 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.internal.telephony; 18 19 import android.annotation.NonNull; 20 import android.app.ActivityManager; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.BroadcastReceiver; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.ServiceConnection; 28 import android.content.pm.ComponentInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.os.Build; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.os.HandlerExecutor; 35 import android.os.IBinder; 36 import android.os.Message; 37 import android.os.Process; 38 import android.os.SystemClock; 39 import android.os.UserHandle; 40 import android.service.carrier.CarrierService; 41 import android.telephony.SubscriptionManager; 42 import android.telephony.TelephonyManager; 43 import android.telephony.TelephonyRegistryManager; 44 import android.text.TextUtils; 45 import android.util.LocalLog; 46 import android.util.Log; 47 import android.util.SparseArray; 48 49 import com.android.internal.annotations.VisibleForTesting; 50 import com.android.internal.telephony.flags.Flags; 51 import com.android.internal.telephony.util.TelephonyUtils; 52 53 import java.io.FileDescriptor; 54 import java.io.PrintWriter; 55 import java.util.Arrays; 56 import java.util.Set; 57 58 /** 59 * Manages long-lived bindings to carrier services 60 * @hide 61 */ 62 public class CarrierServiceBindHelper { 63 private static final String LOG_TAG = "CarrierSvcBindHelper"; 64 65 // TODO(b/201423849): Remove the UNBIND_DELAY_MILLIS and switch to CarrierPrivilegesCallback 66 // The grace period has been replaced by CarrierPrivilegesTracker. CarrierPrivilegesCallback has 67 // provided the callback for both carrier privileges change and carrier service change (with 68 // awareness of the grace period), the delay based logic here should be cleaned up. 69 /** 70 * How long to linger a binding after an app loses carrier privileges, as long as no new 71 * binding comes in to take its place. 72 */ 73 private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds 74 75 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 76 private Context mContext; 77 @VisibleForTesting 78 public SparseArray<AppBinding> mBindings = new SparseArray(); 79 @VisibleForTesting 80 public SparseArray<String> mLastSimState = new SparseArray<>(); 81 // TODO(b/201423849): Clean up PackageChangeReceiver/UserUnlockedReceiver/SIM State change if 82 // CarrierServiceChangeCallback can cover the cases 83 private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor(); 84 private final LocalLog mLocalLog = new LocalLog(100); 85 86 private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { 87 @Override 88 public void onReceive(Context context, Intent intent) { 89 final String action = intent.getAction(); 90 logdWithLocalLog("Received " + action); 91 92 if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 93 // On user unlock, new components might become available, so reevaluate all 94 // bindings. 95 for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) { 96 mBindings.get(phoneId).rebind(); 97 } 98 } 99 } 100 }; 101 102 private class CarrierServiceChangeCallback implements 103 TelephonyManager.CarrierPrivilegesCallback { 104 final int mPhoneId; 105 CarrierServiceChangeCallback(int phoneId)106 CarrierServiceChangeCallback(int phoneId) { 107 this.mPhoneId = phoneId; 108 } 109 110 @Override onCarrierPrivilegesChanged(Set<String> privilegedPackageNames, Set<Integer> privilegedUids)111 public void onCarrierPrivilegesChanged(Set<String> privilegedPackageNames, 112 Set<Integer> privilegedUids) { 113 // Ignored, not interested here 114 } 115 116 @Override onCarrierServiceChanged(String carrierServicePackageName, int carrierServiceUid)117 public void onCarrierServiceChanged(String carrierServicePackageName, 118 int carrierServiceUid) { 119 logdWithLocalLog("onCarrierServiceChanged, carrierServicePackageName=" 120 + carrierServicePackageName + ", carrierServiceUid=" + carrierServiceUid 121 + ", mPhoneId=" + mPhoneId); 122 mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mPhoneId)); 123 } 124 } 125 126 private static final int EVENT_REBIND = 0; 127 @VisibleForTesting 128 public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1; 129 @VisibleForTesting 130 public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2; 131 132 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 133 @VisibleForTesting 134 public Handler mHandler = new Handler() { 135 @Override 136 public void handleMessage(Message msg) { 137 int phoneId; 138 AppBinding binding; 139 logdWithLocalLog("mHandler: " + msg.what); 140 141 switch (msg.what) { 142 case EVENT_REBIND: 143 phoneId = (int) msg.obj; 144 binding = mBindings.get(phoneId); 145 if (binding == null) return; 146 logdWithLocalLog("Rebinding if necessary for phoneId: " + binding.getPhoneId()); 147 binding.rebind(); 148 break; 149 case EVENT_PERFORM_IMMEDIATE_UNBIND: 150 phoneId = (int) msg.obj; 151 binding = mBindings.get(phoneId); 152 if (binding == null) return; 153 logdWithLocalLog("Unbind immediate with phoneId: " + binding.getPhoneId()); 154 binding.performImmediateUnbind(); 155 break; 156 case EVENT_MULTI_SIM_CONFIG_CHANGED: 157 updateBindingsAndSimStates(); 158 break; 159 default: 160 Log.e(LOG_TAG, "Unsupported event received: " + msg.what); 161 } 162 } 163 }; 164 CarrierServiceBindHelper(Context context)165 public CarrierServiceBindHelper(Context context) { 166 mContext = 167 context.createContextAsUser( 168 Flags.supportCarrierServicesForHsum() 169 ? UserHandle.of(ActivityManager.getCurrentUser()) 170 : Process.myUserHandle(), 0); 171 172 updateBindingsAndSimStates(); 173 174 PhoneConfigurationManager.registerForMultiSimConfigChange( 175 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 176 177 mPackageMonitor.register( 178 context, mHandler.getLooper(), UserHandle.ALL); 179 try { 180 Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(), 181 0, Flags.supportCarrierServicesForHsum() ? UserHandle.CURRENT : UserHandle.SYSTEM); 182 contextAsUser.registerReceiver(mUserUnlockedReceiver, 183 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */, 184 mHandler); 185 } catch (PackageManager.NameNotFoundException e) { 186 logeWithLocalLog("Package name not found: " + e.getMessage()); 187 } 188 } 189 190 // Create or dispose mBindings and mLastSimState objects. updateBindingsAndSimStates()191 private void updateBindingsAndSimStates() { 192 int prevLen = mBindings.size(); 193 int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) 194 .getActiveModemCount(); 195 196 // If prevLen < newLen, allocate AppBinding and simState objects. 197 for (int phoneId = prevLen; phoneId < newLen; phoneId++) { 198 mBindings.put(phoneId, new AppBinding(phoneId)); 199 mLastSimState.put(phoneId, new String()); 200 } 201 202 // If prevLen > newLen, dispose AppBinding and simState objects. 203 for (int phoneId = newLen; phoneId < prevLen; phoneId++) { 204 mBindings.get(phoneId).tearDown(); 205 mBindings.get(phoneId).unbind(true); 206 mBindings.delete(phoneId); 207 mLastSimState.delete(phoneId); 208 } 209 } 210 211 /** 212 * Update SIM state. 213 * 214 * @param phoneId The phone id. 215 * @param simState The legacy SIM state. 216 */ updateForPhoneId(int phoneId, @NonNull String simState)217 public void updateForPhoneId(int phoneId, @NonNull String simState) { 218 logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState); 219 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 220 return; 221 } 222 if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return; 223 if (simState.equals(mLastSimState.get(phoneId))) { 224 // ignore consecutive duplicated events 225 return; 226 } else { 227 mLastSimState.put(phoneId, simState); 228 } 229 mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId)); 230 } 231 232 private class AppBinding { 233 private int phoneId; 234 private CarrierServiceConnection connection; 235 private int bindCount; 236 private long lastBindStartMillis; 237 private int unbindCount; 238 private long lastUnbindMillis; 239 private String carrierPackage; 240 private String carrierServiceClass; 241 private long mUnbindScheduledUptimeMillis = -1; 242 private final CarrierServiceChangeCallback mCarrierServiceChangeCallback; 243 AppBinding(int phoneId)244 public AppBinding(int phoneId) { 245 this.phoneId = phoneId; 246 this.mCarrierServiceChangeCallback = new CarrierServiceChangeCallback(phoneId); 247 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 248 if (tm != null) { 249 tm.registerCarrierPrivilegesCallback(phoneId, new HandlerExecutor(mHandler), 250 mCarrierServiceChangeCallback); 251 } 252 } 253 tearDown()254 public void tearDown() { 255 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 256 if (tm != null && mCarrierServiceChangeCallback != null) { 257 tm.unregisterCarrierPrivilegesCallback(mCarrierServiceChangeCallback); 258 } 259 } 260 getPhoneId()261 public int getPhoneId() { 262 return phoneId; 263 } 264 265 /** Return the package that is currently being bound to, or null if there is no binding. */ getPackage()266 public String getPackage() { 267 return carrierPackage; 268 } 269 270 /** 271 * Update the bindings for the current carrier app for this phone. 272 * 273 * <p>Safe to call even if a binding already exists. If the current binding is invalid, it 274 * will be dropped. If it is valid, it will be left untouched. 275 */ rebind()276 void rebind() { 277 // Get the package name for the carrier app 278 String carrierPackageName = TelephonyManager.from( 279 mContext).getCarrierServicePackageNameForLogicalSlot(phoneId); 280 281 if (carrierPackageName == null) { 282 logdWithLocalLog("No carrier app for: " + phoneId); 283 // Unbind after a delay in case this is a temporary blip in carrier privileges. 284 unbind(false /* immediate */); 285 return; 286 } 287 288 logdWithLocalLog("Found carrier app: " + carrierPackageName); 289 // If we are binding to a different package, unbind immediately from the current one. 290 if (!TextUtils.equals(carrierPackage, carrierPackageName)) { 291 unbind(true /* immediate */); 292 } 293 294 // Look up the carrier service 295 Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE); 296 carrierService.setPackage(carrierPackageName); 297 298 ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService( 299 carrierService, PackageManager.GET_META_DATA); 300 Bundle metadata = null; 301 String candidateServiceClass = null; 302 if (carrierResolveInfo != null) { 303 metadata = carrierResolveInfo.serviceInfo.metaData; 304 ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo); 305 candidateServiceClass = new ComponentName(componentInfo.packageName, 306 componentInfo.name).getClassName(); 307 } 308 309 // Only bind if the service wants it 310 if (metadata == null || 311 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) { 312 logdWithLocalLog("Carrier app does not want a long lived binding"); 313 unbind(true /* immediate */); 314 return; 315 } 316 317 if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) { 318 logdWithLocalLog("CarrierService class changed, unbind immediately."); 319 // Unbind immediately if the carrier service component has changed. 320 unbind(true /* immediate */); 321 } else if (connection != null) { 322 logdWithLocalLog( 323 "CarrierService class unchanged with connection up, cancelScheduledUnbind"); 324 // Component is unchanged and connection is up - do nothing, but cancel any 325 // scheduled unbinds. 326 cancelScheduledUnbind(); 327 return; 328 } 329 330 carrierPackage = carrierPackageName; 331 carrierServiceClass = candidateServiceClass; 332 333 logdWithLocalLog("Binding to " + carrierPackage + " for phone " + phoneId); 334 335 // Log debug information 336 bindCount++; 337 lastBindStartMillis = System.currentTimeMillis(); 338 339 connection = new CarrierServiceConnection(getPhoneId()); 340 341 String error; 342 try { 343 if (mContext.bindService( 344 carrierService, 345 Context.BIND_AUTO_CREATE 346 | Context.BIND_FOREGROUND_SERVICE 347 | Context.BIND_INCLUDE_CAPABILITIES, 348 (r) -> mHandler.post(r), 349 connection)) { 350 logdWithLocalLog("service bound"); 351 return; 352 } 353 354 error = "bindService returned false"; 355 } catch (SecurityException ex) { 356 error = ex.getMessage(); 357 } 358 359 logdWithLocalLog("Unable to bind to " + carrierPackage + " for phone " + phoneId 360 + ". Error: " + error); 361 unbind(true /* immediate */); 362 } 363 364 /** 365 * Release the binding. 366 * 367 * @param immediate whether the binding should be released immediately or after a short 368 * delay. This should be true unless the reason for the unbind is that no 369 * app has carrier privileges, in which case it is useful to delay 370 * unbinding in case this is a temporary SIM blip. 371 */ unbind(boolean immediate)372 void unbind(boolean immediate) { 373 if (connection == null) { 374 // Already fully unbound. 375 return; 376 } 377 378 // Only let the binding linger if a delayed unbind is requested *and* the connection is 379 // currently active. If the connection is down, unbind immediately as the app is likely 380 // not running anyway and it may be a permanent disconnection (e.g. the app was 381 // disabled). 382 if (immediate || !connection.connected) { 383 logdWithLocalLog("unbind immediately or with disconnected connection"); 384 cancelScheduledUnbind(); 385 performImmediateUnbind(); 386 } else if (mUnbindScheduledUptimeMillis == -1) { 387 long currentUptimeMillis = SystemClock.uptimeMillis(); 388 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS; 389 logdWithLocalLog("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis"); 390 mHandler.sendMessageAtTime( 391 mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId), 392 mUnbindScheduledUptimeMillis); 393 } 394 } 395 performImmediateUnbind()396 private void performImmediateUnbind() { 397 // Log debug information 398 unbindCount++; 399 lastUnbindMillis = System.currentTimeMillis(); 400 401 // Clear package state now that no binding is desired. 402 carrierPackage = null; 403 carrierServiceClass = null; 404 405 // Always call unbindService, no matter if bindService succeed. 406 if (connection != null) { 407 mContext.unbindService(connection); 408 logdWithLocalLog("Unbinding from carrier app"); 409 connection = null; 410 mUnbindScheduledUptimeMillis = -1; 411 } 412 } 413 cancelScheduledUnbind()414 private void cancelScheduledUnbind() { 415 logdWithLocalLog("cancelScheduledUnbind"); 416 mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND); 417 mUnbindScheduledUptimeMillis = -1; 418 } 419 dump(FileDescriptor fd, PrintWriter pw, String[] args)420 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 421 pw.println("Carrier app binding for phone " + phoneId); 422 pw.println(" connection: " + connection); 423 pw.println(" bindCount: " + bindCount); 424 pw.println(" lastBindStartMillis: " + lastBindStartMillis); 425 pw.println(" unbindCount: " + unbindCount); 426 pw.println(" lastUnbindMillis: " + lastUnbindMillis); 427 pw.println(" mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis); 428 pw.println(" mCarrierServiceChangeCallback: " + mCarrierServiceChangeCallback); 429 pw.println(); 430 } 431 } 432 433 private class CarrierServiceConnection implements ServiceConnection { 434 private boolean connected; 435 private final int mPhoneId; 436 CarrierServiceConnection(int phoneId)437 CarrierServiceConnection(int phoneId) { 438 mPhoneId = phoneId; 439 } 440 441 @Override onServiceConnected(ComponentName name, IBinder service)442 public void onServiceConnected(ComponentName name, IBinder service) { 443 logdWithLocalLog("Connected to carrier app: " + name.flattenToString()); 444 connected = true; 445 } 446 maybeDisableCarrierNetworkChangeNotification()447 private void maybeDisableCarrierNetworkChangeNotification() { 448 int subscriptionId = SubscriptionManager.getSubscriptionId(mPhoneId); 449 // TODO(b/117525047): switch to phoneId-based solution when available in 450 // TelephonyRegistryManager to address SIM remove/disable case. 451 if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { 452 logdWithLocalLog( 453 "No valid subscription found when trying to disable carrierNetworkChange" 454 + " for phoneId: " 455 + mPhoneId); 456 return; 457 } 458 TelephonyRegistryManager telephonyRegistryManager = 459 mContext.getSystemService(TelephonyRegistryManager.class); 460 telephonyRegistryManager.notifyCarrierNetworkChange(subscriptionId, false); 461 } 462 463 @Override onServiceDisconnected(ComponentName name)464 public void onServiceDisconnected(ComponentName name) { 465 logdWithLocalLog("Disconnected from carrier app: " + name.flattenToString()); 466 connected = false; 467 if (Flags.disableCarrierNetworkChangeOnCarrierAppLost()) { 468 maybeDisableCarrierNetworkChangeNotification(); 469 } 470 } 471 472 @Override onBindingDied(ComponentName name)473 public void onBindingDied(ComponentName name) { 474 logdWithLocalLog("Binding from carrier app died: " + name.flattenToString()); 475 connected = false; 476 if (Flags.disableCarrierNetworkChangeOnCarrierAppLost()) { 477 maybeDisableCarrierNetworkChangeNotification(); 478 } 479 } 480 481 @Override onNullBinding(ComponentName name)482 public void onNullBinding(ComponentName name) { 483 logdWithLocalLog("Null binding from carrier app: " + name.flattenToString()); 484 connected = false; 485 } 486 487 @Override toString()488 public String toString() { 489 return "CarrierServiceConnection[connected=" + connected + "]"; 490 } 491 } 492 493 private class CarrierServicePackageMonitor extends PackageChangeReceiver { 494 @Override onPackageAdded(String packageName)495 public void onPackageAdded(String packageName) { 496 logdWithLocalLog("onPackageAdded: " + packageName); 497 evaluateBinding(packageName, true /* forceUnbind */); 498 } 499 500 @Override onPackageRemoved(String packageName)501 public void onPackageRemoved(String packageName) { 502 logdWithLocalLog("onPackageRemoved: " + packageName); 503 evaluateBinding(packageName, true /* forceUnbind */); 504 } 505 506 @Override onPackageUpdateFinished(String packageName)507 public void onPackageUpdateFinished(String packageName) { 508 logdWithLocalLog("onPackageUpdateFinished: " + packageName); 509 evaluateBinding(packageName, true /* forceUnbind */); 510 } 511 512 @Override onPackageModified(String packageName)513 public void onPackageModified(String packageName) { 514 logdWithLocalLog("onPackageModified: " + packageName); 515 evaluateBinding(packageName, false /* forceUnbind */); 516 } 517 518 @Override onHandleForceStop(String[] packages, boolean doit)519 public void onHandleForceStop(String[] packages, boolean doit) { 520 if (doit) { 521 logdWithLocalLog("onHandleForceStop: " + Arrays.toString(packages)); 522 for (String packageName : packages) { 523 evaluateBinding(packageName, true /* forceUnbind */); 524 } 525 } 526 } 527 evaluateBinding(String carrierPackageName, boolean forceUnbind)528 private void evaluateBinding(String carrierPackageName, boolean forceUnbind) { 529 for (int i = 0; i < mBindings.size(); i++) { 530 AppBinding appBinding = mBindings.get(i); 531 String appBindingPackage = appBinding.getPackage(); 532 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage); 533 // Only log if this package was a carrier package to avoid log spam in the common 534 // case that there are no carrier packages, but evaluate the binding if the package 535 // is unset, in case this package change resulted in a new carrier package becoming 536 // available for binding. 537 if (isBindingForPackage) { 538 logdWithLocalLog( 539 carrierPackageName + " changed and corresponds to a phone. Rebinding."); 540 } 541 if (appBindingPackage == null || isBindingForPackage) { 542 if (forceUnbind) { 543 appBinding.unbind(true /* immediate */); 544 } 545 appBinding.rebind(); 546 } 547 } 548 } 549 } 550 logdWithLocalLog(String msg)551 private void logdWithLocalLog(String msg) { 552 Log.d(LOG_TAG, msg); 553 mLocalLog.log(msg); 554 } 555 logeWithLocalLog(String msg)556 private void logeWithLocalLog(String msg) { 557 Log.e(LOG_TAG, msg); 558 mLocalLog.log(msg); 559 } 560 dump(FileDescriptor fd, PrintWriter pw, String[] args)561 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 562 pw.println("CarrierServiceBindHelper:"); 563 for (int i = 0; i < mBindings.size(); i++) { 564 mBindings.get(i).dump(fd, pw, args); 565 } 566 pw.println("CarrierServiceBindHelperLogs="); 567 mLocalLog.dump(fd, pw, args); 568 } 569 } 570