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