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