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.compat.annotation.UnsupportedAppUsage; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.ServiceConnection; 26 import android.content.pm.ComponentInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ResolveInfo; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Message; 33 import android.os.Process; 34 import android.os.SystemClock; 35 import android.os.UserHandle; 36 import android.service.carrier.CarrierService; 37 import android.telephony.PackageChangeReceiver; 38 import android.telephony.SubscriptionManager; 39 import android.telephony.TelephonyManager; 40 import android.text.TextUtils; 41 import android.util.Log; 42 import android.util.SparseArray; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.telephony.util.TelephonyUtils; 46 47 import java.io.FileDescriptor; 48 import java.io.PrintWriter; 49 import java.util.List; 50 51 /** 52 * Manages long-lived bindings to carrier services 53 * @hide 54 */ 55 public class CarrierServiceBindHelper { 56 private static final String LOG_TAG = "CarrierSvcBindHelper"; 57 58 /** 59 * How long to linger a binding after an app loses carrier privileges, as long as no new 60 * binding comes in to take its place. 61 */ 62 private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds 63 64 @UnsupportedAppUsage 65 private Context mContext; 66 @VisibleForTesting 67 public SparseArray<AppBinding> mBindings = new SparseArray(); 68 @VisibleForTesting 69 public SparseArray<String> mLastSimState = new SparseArray<>(); 70 private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor(); 71 72 // whether we have successfully bound to the service 73 private boolean mServiceBound = false; 74 75 private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { 76 @Override 77 public void onReceive(Context context, Intent intent) { 78 final String action = intent.getAction(); 79 log("Received " + action); 80 81 if (Intent.ACTION_USER_UNLOCKED.equals(action)) { 82 // On user unlock, new components might become available, so reevaluate all 83 // bindings. 84 for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) { 85 mBindings.get(phoneId).rebind(); 86 } 87 } 88 } 89 }; 90 91 private static final int EVENT_REBIND = 0; 92 @VisibleForTesting 93 public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1; 94 @VisibleForTesting 95 public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2; 96 97 @UnsupportedAppUsage 98 @VisibleForTesting 99 public Handler mHandler = new Handler() { 100 @Override 101 public void handleMessage(Message msg) { 102 int phoneId; 103 AppBinding binding; 104 log("mHandler: " + msg.what); 105 106 switch (msg.what) { 107 case EVENT_REBIND: 108 phoneId = (int) msg.obj; 109 binding = mBindings.get(phoneId); 110 if (binding == null) return; 111 log("Rebinding if necessary for phoneId: " + binding.getPhoneId()); 112 binding.rebind(); 113 break; 114 case EVENT_PERFORM_IMMEDIATE_UNBIND: 115 phoneId = (int) msg.obj; 116 binding = mBindings.get(phoneId); 117 if (binding == null) return; 118 binding.performImmediateUnbind(); 119 break; 120 case EVENT_MULTI_SIM_CONFIG_CHANGED: 121 updateBindingsAndSimStates(); 122 break; 123 } 124 } 125 }; 126 CarrierServiceBindHelper(Context context)127 public CarrierServiceBindHelper(Context context) { 128 mContext = context; 129 130 updateBindingsAndSimStates(); 131 132 PhoneConfigurationManager.registerForMultiSimConfigChange( 133 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null); 134 135 mPackageMonitor.register( 136 context, mHandler.getLooper(), UserHandle.ALL); 137 try { 138 Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(), 139 0, UserHandle.SYSTEM); 140 contextAsUser.registerReceiver(mUserUnlockedReceiver, 141 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */, 142 mHandler); 143 } catch (PackageManager.NameNotFoundException e) { 144 loge("Package name not found: " + e.getMessage()); 145 } 146 } 147 148 // Create or dispose mBindings and mLastSimState objects. updateBindingsAndSimStates()149 private void updateBindingsAndSimStates() { 150 int prevLen = mBindings.size(); 151 int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) 152 .getActiveModemCount(); 153 154 // If prevLen < newLen, allocate AppBinding and simState objects. 155 for (int phoneId = prevLen; phoneId < newLen; phoneId++) { 156 mBindings.put(phoneId, new AppBinding(phoneId)); 157 mLastSimState.put(phoneId, new String()); 158 } 159 160 // If prevLen > newLen, dispose AppBinding and simState objects. 161 for (int phoneId = newLen; phoneId < prevLen; phoneId++) { 162 mBindings.get(phoneId).unbind(true); 163 mBindings.delete(phoneId); 164 mLastSimState.delete(phoneId); 165 } 166 } 167 updateForPhoneId(int phoneId, String simState)168 void updateForPhoneId(int phoneId, String simState) { 169 log("update binding for phoneId: " + phoneId + " simState: " + simState); 170 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 171 return; 172 } 173 if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return; 174 if (simState.equals(mLastSimState.get(phoneId))) { 175 // ignore consecutive duplicated events 176 return; 177 } else { 178 mLastSimState.put(phoneId, simState); 179 } 180 mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId)); 181 } 182 183 private class AppBinding { 184 private int phoneId; 185 private CarrierServiceConnection connection; 186 private int bindCount; 187 private long lastBindStartMillis; 188 private int unbindCount; 189 private long lastUnbindMillis; 190 private String carrierPackage; 191 private String carrierServiceClass; 192 private long mUnbindScheduledUptimeMillis = -1; 193 AppBinding(int phoneId)194 public AppBinding(int phoneId) { 195 this.phoneId = phoneId; 196 } 197 getPhoneId()198 public int getPhoneId() { 199 return phoneId; 200 } 201 202 /** Return the package that is currently being bound to, or null if there is no binding. */ getPackage()203 public String getPackage() { 204 return carrierPackage; 205 } 206 207 /** 208 * Update the bindings for the current carrier app for this phone. 209 * 210 * <p>Safe to call even if a binding already exists. If the current binding is invalid, it 211 * will be dropped. If it is valid, it will be left untouched. 212 */ rebind()213 void rebind() { 214 // Get the package name for the carrier app 215 List<String> carrierPackageNames = 216 TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone( 217 new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId 218 ); 219 220 if (carrierPackageNames == null || carrierPackageNames.size() <= 0) { 221 log("No carrier app for: " + phoneId); 222 // Unbind after a delay in case this is a temporary blip in carrier privileges. 223 unbind(false /* immediate */); 224 return; 225 } 226 227 log("Found carrier app: " + carrierPackageNames); 228 String candidateCarrierPackage = carrierPackageNames.get(0); 229 // If we are binding to a different package, unbind immediately from the current one. 230 if (!TextUtils.equals(carrierPackage, candidateCarrierPackage)) { 231 unbind(true /* immediate */); 232 } 233 234 // Look up the carrier service 235 Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE); 236 carrierService.setPackage(candidateCarrierPackage); 237 238 ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService( 239 carrierService, PackageManager.GET_META_DATA); 240 Bundle metadata = null; 241 String candidateServiceClass = null; 242 if (carrierResolveInfo != null) { 243 metadata = carrierResolveInfo.serviceInfo.metaData; 244 ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo); 245 candidateServiceClass = new ComponentName(componentInfo.packageName, 246 componentInfo.name).getClassName(); 247 } 248 249 // Only bind if the service wants it 250 if (metadata == null || 251 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) { 252 log("Carrier app does not want a long lived binding"); 253 unbind(true /* immediate */); 254 return; 255 } 256 257 if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) { 258 // Unbind immediately if the carrier service component has changed. 259 unbind(true /* immediate */); 260 } else if (connection != null) { 261 // Component is unchanged and connection is up - do nothing, but cancel any 262 // scheduled unbinds. 263 cancelScheduledUnbind(); 264 return; 265 } 266 267 carrierPackage = candidateCarrierPackage; 268 carrierServiceClass = candidateServiceClass; 269 270 log("Binding to " + carrierPackage + " for phone " + phoneId); 271 272 // Log debug information 273 bindCount++; 274 lastBindStartMillis = System.currentTimeMillis(); 275 276 connection = new CarrierServiceConnection(); 277 278 String error; 279 try { 280 if (mContext.createContextAsUser(Process.myUserHandle(), 0) 281 .bindService(carrierService, 282 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, 283 (r) -> mHandler.post(r), 284 connection)) { 285 log("service bound"); 286 mServiceBound = true; 287 return; 288 } 289 290 error = "bindService returned false"; 291 } catch (SecurityException ex) { 292 error = ex.getMessage(); 293 } 294 295 log("Unable to bind to " + carrierPackage + " for phone " + phoneId + 296 ". Error: " + error); 297 unbind(true /* immediate */); 298 } 299 300 /** 301 * Release the binding. 302 * 303 * @param immediate whether the binding should be released immediately or after a short 304 * delay. This should be true unless the reason for the unbind is that no 305 * app has carrier privileges, in which case it is useful to delay 306 * unbinding in case this is a temporary SIM blip. 307 */ unbind(boolean immediate)308 void unbind(boolean immediate) { 309 if (connection == null) { 310 // Already fully unbound. 311 return; 312 } 313 314 // Only let the binding linger if a delayed unbind is requested *and* the connection is 315 // currently active. If the connection is down, unbind immediately as the app is likely 316 // not running anyway and it may be a permanent disconnection (e.g. the app was 317 // disabled). 318 if (immediate || !connection.connected) { 319 cancelScheduledUnbind(); 320 performImmediateUnbind(); 321 } else if (mUnbindScheduledUptimeMillis == -1) { 322 long currentUptimeMillis = SystemClock.uptimeMillis(); 323 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS; 324 log("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis"); 325 mHandler.sendMessageAtTime( 326 mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId), 327 mUnbindScheduledUptimeMillis); 328 } 329 } 330 performImmediateUnbind()331 private void performImmediateUnbind() { 332 // Log debug information 333 unbindCount++; 334 lastUnbindMillis = System.currentTimeMillis(); 335 336 // Clear package state now that no binding is desired. 337 carrierPackage = null; 338 carrierServiceClass = null; 339 340 // Actually unbind 341 if (mServiceBound) { 342 log("Unbinding from carrier app"); 343 mServiceBound = false; 344 try { 345 mContext.unbindService(connection); 346 } catch (IllegalArgumentException e) { 347 //TODO(b/151328766): Figure out why we unbind without binding 348 loge("Tried to unbind without binding e=" + e); 349 } 350 } else { 351 log("Not bound, skipping unbindService call"); 352 } 353 connection = null; 354 mUnbindScheduledUptimeMillis = -1; 355 } 356 cancelScheduledUnbind()357 private void cancelScheduledUnbind() { 358 mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND); 359 mUnbindScheduledUptimeMillis = -1; 360 } 361 dump(FileDescriptor fd, PrintWriter pw, String[] args)362 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 363 pw.println("Carrier app binding for phone " + phoneId); 364 pw.println(" connection: " + connection); 365 pw.println(" bindCount: " + bindCount); 366 pw.println(" lastBindStartMillis: " + lastBindStartMillis); 367 pw.println(" unbindCount: " + unbindCount); 368 pw.println(" lastUnbindMillis: " + lastUnbindMillis); 369 pw.println(" mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis); 370 pw.println(); 371 } 372 } 373 374 private class CarrierServiceConnection implements ServiceConnection { 375 private boolean connected; 376 377 @Override onServiceConnected(ComponentName name, IBinder service)378 public void onServiceConnected(ComponentName name, IBinder service) { 379 log("Connected to carrier app: " + name.flattenToString()); 380 connected = true; 381 } 382 383 @Override onServiceDisconnected(ComponentName name)384 public void onServiceDisconnected(ComponentName name) { 385 log("Disconnected from carrier app: " + name.flattenToString()); 386 connected = false; 387 } 388 389 @Override onBindingDied(ComponentName name)390 public void onBindingDied(ComponentName name) { 391 log("Binding from carrier app died: " + name.flattenToString()); 392 connected = false; 393 } 394 395 @Override onNullBinding(ComponentName name)396 public void onNullBinding(ComponentName name) { 397 log("Null binding from carrier app: " + name.flattenToString()); 398 connected = false; 399 } 400 401 @Override toString()402 public String toString() { 403 return "CarrierServiceConnection[connected=" + connected + "]"; 404 } 405 } 406 407 private class CarrierServicePackageMonitor extends PackageChangeReceiver { 408 @Override onPackageAdded(String packageName)409 public void onPackageAdded(String packageName) { 410 evaluateBinding(packageName, true /* forceUnbind */); 411 } 412 413 @Override onPackageRemoved(String packageName)414 public void onPackageRemoved(String packageName) { 415 evaluateBinding(packageName, true /* forceUnbind */); 416 } 417 418 @Override onPackageUpdateFinished(String packageName)419 public void onPackageUpdateFinished(String packageName) { 420 evaluateBinding(packageName, true /* forceUnbind */); 421 } 422 423 @Override onPackageModified(String packageName)424 public void onPackageModified(String packageName) { 425 evaluateBinding(packageName, false /* forceUnbind */); 426 } 427 428 @Override onHandleForceStop(String[] packages, boolean doit)429 public void onHandleForceStop(String[] packages, boolean doit) { 430 if (doit) { 431 for (String packageName : packages) { 432 evaluateBinding(packageName, true /* forceUnbind */); 433 } 434 } 435 } 436 evaluateBinding(String carrierPackageName, boolean forceUnbind)437 private void evaluateBinding(String carrierPackageName, boolean forceUnbind) { 438 for (int i = 0; i < mBindings.size(); i++) { 439 AppBinding appBinding = mBindings.get(i); 440 String appBindingPackage = appBinding.getPackage(); 441 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage); 442 // Only log if this package was a carrier package to avoid log spam in the common 443 // case that there are no carrier packages, but evaluate the binding if the package 444 // is unset, in case this package change resulted in a new carrier package becoming 445 // available for binding. 446 if (isBindingForPackage) { 447 log(carrierPackageName + " changed and corresponds to a phone. Rebinding."); 448 } 449 if (appBindingPackage == null || isBindingForPackage) { 450 if (forceUnbind) { 451 appBinding.unbind(true /* immediate */); 452 } 453 appBinding.rebind(); 454 } 455 } 456 } 457 } 458 log(String message)459 private static void log(String message) { 460 Log.d(LOG_TAG, message); 461 } 462 loge(String message)463 private static void loge(String message) { Log.e(LOG_TAG, message); } 464 dump(FileDescriptor fd, PrintWriter pw, String[] args)465 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 466 pw.println("CarrierServiceBindHelper:"); 467 for (int i = 0; i < mBindings.size(); i++) { 468 mBindings.get(i).dump(fd, pw, args); 469 } 470 } 471 } 472