1 /* 2 * Copyright 2020 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.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.Build; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.os.RemoteException; 30 import android.telephony.IBootstrapAuthenticationCallback; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.TelephonyManager; 33 import android.telephony.gba.GbaAuthRequest; 34 import android.telephony.gba.GbaService; 35 import android.telephony.gba.IGbaService; 36 import android.text.TextUtils; 37 import android.util.SparseArray; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.metrics.RcsStats; 41 import com.android.telephony.Rlog; 42 43 import java.util.NoSuchElementException; 44 import java.util.concurrent.ConcurrentLinkedQueue; 45 46 /** 47 * Class that serves as the layer between GbaService and ServiceStateTracker. It helps binding, 48 * sending request, receiving callback, and registering for state change to GbaService. 49 */ 50 public class GbaManager { 51 private static final boolean DBG = Build.IS_DEBUGGABLE; 52 private static final int EVENT_BIND_SERVICE = 1; 53 private static final int EVENT_UNBIND_SERVICE = 2; 54 private static final int EVENT_BIND_FAIL = 3; 55 private static final int EVENT_BIND_SUCCESS = 4; 56 private static final int EVENT_CONFIG_CHANGED = 5; 57 private static final int EVENT_REQUESTS_RECEIVED = 6; 58 59 @VisibleForTesting 60 public static final int RETRY_TIME_MS = 3000; 61 @VisibleForTesting 62 public static final int MAX_RETRY = 5; 63 @VisibleForTesting 64 public static final int REQUEST_TIMEOUT_MS = 5000; 65 private final RcsStats mRcsStats; 66 67 private final String mLogTag; 68 private final Context mContext; 69 private final int mSubId; 70 71 private IGbaService mIGbaService; 72 private GbaDeathRecipient mDeathRecipient; 73 private String mTargetBindingPackageName; 74 private GbaServiceConnection mServiceConnection; 75 private Handler mHandler; 76 77 private String mServicePackageName; 78 private String mServicePackageNameOverride; 79 private int mReleaseTime; 80 private int mRetryTimes = 0; 81 82 //the requests to be sent to the GBA service 83 private final ConcurrentLinkedQueue<GbaAuthRequest> mRequestQueue = 84 new ConcurrentLinkedQueue<>(); 85 //the callbacks of the pending requests which have been sent to the GBA service 86 private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>(); 87 88 private static final SparseArray<GbaManager> sGbaManagers = new SparseArray<>(); 89 90 private final class GbaManagerHandler extends Handler { GbaManagerHandler(Looper looper)91 GbaManagerHandler(Looper looper) { 92 super(looper); 93 } 94 95 @Override handleMessage(Message msg)96 public void handleMessage(Message msg) { 97 logv("handle msg:" + msg.what); 98 switch (msg.what) { 99 case EVENT_BIND_SERVICE: 100 if (mRetryTimes++ < MAX_RETRY) { 101 rebindService(false); 102 } else { 103 loge("Too many retries, stop now!"); 104 sendEmptyMessage(EVENT_BIND_FAIL); 105 } 106 break; 107 case EVENT_UNBIND_SERVICE: 108 //do nothing if new requests are coming 109 if (mRequestQueue.isEmpty()) { 110 clearCallbacksAndNotifyFailure(); 111 unbindService(); 112 } 113 break; 114 case EVENT_BIND_FAIL: 115 case EVENT_BIND_SUCCESS: 116 mRetryTimes = 0; 117 processRequests(); 118 break; 119 case EVENT_REQUESTS_RECEIVED: 120 if (isServiceConnected()) { 121 processRequests(); 122 } else { 123 if (!mHandler.hasMessages(EVENT_BIND_SERVICE)) { 124 mHandler.sendEmptyMessage(EVENT_BIND_SERVICE); 125 } 126 } 127 break; 128 case EVENT_CONFIG_CHANGED: 129 mRetryTimes = 0; 130 if (isServiceConnetable() || isServiceConnected()) { 131 //force to rebind when config is changed 132 rebindService(true); 133 } 134 break; 135 default: 136 loge("Unhandled event " + msg.what); 137 } 138 } 139 } 140 141 private final class GbaDeathRecipient implements IBinder.DeathRecipient { 142 143 private final ComponentName mComponentName; 144 private IBinder mBinder; 145 GbaDeathRecipient(ComponentName name)146 GbaDeathRecipient(ComponentName name) { 147 mComponentName = name; 148 } 149 linkToDeath(IBinder service)150 public void linkToDeath(IBinder service) throws RemoteException { 151 if (service != null) { 152 mBinder = service; 153 mBinder.linkToDeath(this, 0); 154 } 155 } 156 unlinkToDeath()157 public synchronized void unlinkToDeath() { 158 if (mBinder != null) { 159 try { 160 mBinder.unlinkToDeath(this, 0); 161 } catch (NoSuchElementException e) { 162 // do nothing 163 } 164 mBinder = null; 165 } 166 } 167 168 @Override binderDied()169 public void binderDied() { 170 logd("GbaService(" + mComponentName + ") has died."); 171 unlinkToDeath(); 172 //retry if died 173 retryBind(); 174 } 175 } 176 177 private final class GbaServiceConnection implements ServiceConnection { 178 @Override onServiceConnected(ComponentName name, IBinder service)179 public void onServiceConnected(ComponentName name, IBinder service) { 180 logd("service " + name + " for Gba is connected."); 181 mIGbaService = IGbaService.Stub.asInterface(service); 182 mDeathRecipient = new GbaDeathRecipient(name); 183 try { 184 mDeathRecipient.linkToDeath(service); 185 } catch (RemoteException exception) { 186 // Remote exception means that the binder already died. 187 mDeathRecipient.binderDied(); 188 logd("RemoteException " + exception); 189 } 190 mHandler.sendEmptyMessage(EVENT_BIND_SUCCESS); 191 } 192 193 @Override onServiceDisconnected(ComponentName name)194 public void onServiceDisconnected(ComponentName name) { 195 logd("service " + name + " is now disconnected."); 196 mTargetBindingPackageName = null; 197 } 198 } 199 200 @VisibleForTesting GbaManager(Context context, int subId, String servicePackageName, int releaseTime, RcsStats rcsStats)201 public GbaManager(Context context, int subId, String servicePackageName, int releaseTime, 202 RcsStats rcsStats) { 203 mContext = context; 204 mSubId = subId; 205 mLogTag = "GbaManager[" + subId + "]"; 206 207 mServicePackageName = servicePackageName; 208 mReleaseTime = releaseTime; 209 210 HandlerThread headlerThread = new HandlerThread(mLogTag); 211 headlerThread.start(); 212 mHandler = new GbaManagerHandler(headlerThread.getLooper()); 213 214 if (mReleaseTime < 0) { 215 mHandler.sendEmptyMessage(EVENT_BIND_SERVICE); 216 } 217 mRcsStats = rcsStats; 218 } 219 220 /** 221 * create a GbaManager instance for a sub 222 */ make(Context context, int subId, String servicePackageName, int releaseTime)223 public static GbaManager make(Context context, int subId, 224 String servicePackageName, int releaseTime) { 225 GbaManager gm = new GbaManager(context, subId, servicePackageName, releaseTime, 226 RcsStats.getInstance()); 227 synchronized (sGbaManagers) { 228 sGbaManagers.put(subId, gm); 229 } 230 return gm; 231 } 232 233 /** 234 * get singleton instance of GbaManager 235 * @return GbaManager 236 */ getInstance(int subId)237 public static GbaManager getInstance(int subId) { 238 synchronized (sGbaManagers) { 239 return sGbaManagers.get(subId); 240 } 241 } 242 243 /** 244 * handle the bootstrap authentication request 245 * @hide 246 */ bootstrapAuthenticationRequest(GbaAuthRequest req)247 public void bootstrapAuthenticationRequest(GbaAuthRequest req) { 248 logv("bootstrapAuthenticationRequest: " + req); 249 //No GBA service configured 250 if (TextUtils.isEmpty(getServicePackage())) { 251 logd("do not support!"); 252 try { 253 req.getCallback().onAuthenticationFailure(req.getToken(), 254 TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); 255 } catch (RemoteException exception) { 256 loge("exception to call service: " + exception); 257 } 258 return; 259 } 260 261 mRequestQueue.offer(req); 262 if (!mHandler.hasMessages(EVENT_REQUESTS_RECEIVED)) { 263 mHandler.sendEmptyMessage(EVENT_REQUESTS_RECEIVED); 264 } 265 } 266 267 private final IBootstrapAuthenticationCallback mServiceCallback = 268 new IBootstrapAuthenticationCallback.Stub() { 269 @Override 270 public void onKeysAvailable(int token, byte[] gbaKey, String btId) { 271 logv("onKeysAvailable: " + Integer.toHexString(token) + ", id: " + btId); 272 273 IBootstrapAuthenticationCallback cb = null; 274 synchronized (mCallbacks) { 275 cb = mCallbacks.get(token); 276 } 277 if (cb != null) { 278 try { 279 cb.onKeysAvailable(token, gbaKey, btId); 280 mRcsStats.onGbaSuccessEvent(mSubId); 281 } catch (RemoteException exception) { 282 logd("RemoteException " + exception); 283 } 284 synchronized (mCallbacks) { 285 mCallbacks.remove(token); 286 if (mCallbacks.size() == 0) { 287 releaseServiceAsNeeded(0); 288 } 289 } 290 } 291 } 292 293 @Override 294 public void onAuthenticationFailure(int token, int reason) { 295 logd("onAuthenticationFailure: " 296 + Integer.toHexString(token) + " for: " + reason); 297 298 IBootstrapAuthenticationCallback cb = null; 299 synchronized (mCallbacks) { 300 cb = mCallbacks.get(token); 301 } 302 if (cb != null) { 303 try { 304 cb.onAuthenticationFailure(token, reason); 305 mRcsStats.onGbaFailureEvent(mSubId, reason); 306 } catch (RemoteException exception) { 307 logd("RemoteException " + exception); 308 } 309 synchronized (mCallbacks) { 310 mCallbacks.remove(token); 311 if (mCallbacks.size() == 0) { 312 releaseServiceAsNeeded(0); 313 } 314 } 315 } 316 } 317 }; 318 processRequests()319 private void processRequests() { 320 if (isServiceConnected()) { 321 try { 322 while (!mRequestQueue.isEmpty()) { 323 GbaAuthRequest request = new GbaAuthRequest(mRequestQueue.peek()); 324 synchronized (mCallbacks) { 325 mCallbacks.put(request.getToken(), request.getCallback()); 326 } 327 request.setCallback(mServiceCallback); 328 mIGbaService.authenticationRequest(request); 329 mRequestQueue.poll(); 330 } 331 } catch (RemoteException exception) { 332 // Remote exception means that the binder already died. 333 mDeathRecipient.binderDied(); 334 logd("RemoteException " + exception); 335 } 336 } else { 337 while (!mRequestQueue.isEmpty()) { 338 GbaAuthRequest req = mRequestQueue.poll(); 339 try { 340 req.getCallback().onAuthenticationFailure(req.getToken(), 341 TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); 342 } catch (RemoteException exception) { 343 logd("RemoteException " + exception); 344 } 345 } 346 } 347 348 releaseServiceAsNeeded(REQUEST_TIMEOUT_MS); 349 } 350 releaseServiceAsNeeded(int timeout)351 private void releaseServiceAsNeeded(int timeout) { 352 int configReleaseTime = getReleaseTime(); 353 //always on 354 if (configReleaseTime < 0) { 355 return; 356 } 357 //schedule to release service 358 int delayTime = configReleaseTime > timeout ? configReleaseTime : timeout; 359 if (mHandler.hasMessages(EVENT_UNBIND_SERVICE)) { 360 mHandler.removeMessages(EVENT_UNBIND_SERVICE); 361 } 362 mHandler.sendEmptyMessageDelayed(EVENT_UNBIND_SERVICE, delayTime); 363 } 364 clearCallbacksAndNotifyFailure()365 private void clearCallbacksAndNotifyFailure() { 366 synchronized (mCallbacks) { 367 for (int i = 0; i < mCallbacks.size(); i++) { 368 IBootstrapAuthenticationCallback cb = mCallbacks.valueAt(i); 369 try { 370 cb.onAuthenticationFailure(mCallbacks.keyAt(i), 371 TelephonyManager.GBA_FAILURE_REASON_UNKNOWN); 372 } catch (RemoteException exception) { 373 logd("RemoteException " + exception); 374 } 375 } 376 mCallbacks.clear(); 377 } 378 } 379 380 /** return if GBA service has been connected */ 381 @VisibleForTesting isServiceConnected()382 public boolean isServiceConnected() { 383 //current bound service should be the updated service package. 384 synchronized (this) { 385 return (mIGbaService != null) && (mIGbaService.asBinder().isBinderAlive()) 386 && TextUtils.equals(mServicePackageName, mTargetBindingPackageName); 387 } 388 } 389 isServiceConnetable()390 private boolean isServiceConnetable() { 391 synchronized (this) { 392 return mTargetBindingPackageName != null || ( 393 mReleaseTime < 0 && !TextUtils.isEmpty(mServicePackageName)); 394 } 395 } 396 unbindService()397 private void unbindService() { 398 if (mDeathRecipient != null) { 399 mDeathRecipient.unlinkToDeath(); 400 } 401 if (mServiceConnection != null) { 402 logv("unbind service."); 403 mContext.unbindService(mServiceConnection); 404 } 405 mDeathRecipient = null; 406 mIGbaService = null; 407 mServiceConnection = null; 408 mTargetBindingPackageName = null; 409 } 410 bindService()411 private void bindService() { 412 if (mContext == null || !SubscriptionManager.isValidSubscriptionId(mSubId)) { 413 loge("Can't bind service with invalid sub Id."); 414 return; 415 } 416 417 String servicePackage = getServicePackage(); 418 if (TextUtils.isEmpty(servicePackage)) { 419 loge("Can't find the binding package"); 420 return; 421 } 422 423 Intent intent = new Intent(GbaService.SERVICE_INTERFACE); 424 intent.setPackage(servicePackage); 425 426 try { 427 logv("Trying to bind " + servicePackage); 428 mServiceConnection = new GbaServiceConnection(); 429 if (!mContext.bindService(intent, mServiceConnection, 430 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)) { 431 logd("Cannot bind to the service."); 432 retryBind(); 433 return; 434 } 435 mTargetBindingPackageName = servicePackage; 436 } catch (SecurityException exception) { 437 loge("bindService failed " + exception); 438 } 439 } 440 retryBind()441 private void retryBind() { 442 //do nothing if binding service has been scheduled 443 if (mHandler.hasMessages(EVENT_BIND_SERVICE)) { 444 logv("wait for pending retry."); 445 return; 446 } 447 448 logv("starting retry:" + mRetryTimes); 449 450 mHandler.sendEmptyMessageDelayed(EVENT_BIND_SERVICE, RETRY_TIME_MS); 451 } 452 rebindService(boolean isForce)453 private void rebindService(boolean isForce) { 454 // Do nothing if no need to rebind. 455 if (!isForce && isServiceConnected()) { 456 logv("Service " + getServicePackage() + " already bound or being bound."); 457 return; 458 } 459 460 unbindService(); 461 bindService(); 462 } 463 464 /** override GBA service package name to be connected */ overrideServicePackage(String packageName)465 public boolean overrideServicePackage(String packageName) { 466 synchronized (this) { 467 if (!TextUtils.equals(mServicePackageName, packageName)) { 468 logv("Service package name is changed from " + mServicePackageName 469 + " to " + packageName); 470 mServicePackageName = packageName; 471 if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) { 472 mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED); 473 } 474 return true; 475 } 476 } 477 return false; 478 } 479 480 /** return GBA service package name */ getServicePackage()481 public String getServicePackage() { 482 synchronized (this) { 483 return mServicePackageName; 484 } 485 } 486 487 /** override the release time to unbind GBA service after the request is handled */ overrideReleaseTime(int interval)488 public boolean overrideReleaseTime(int interval) { 489 synchronized (this) { 490 if (mReleaseTime != interval) { 491 logv("Service release time is changed from " + mReleaseTime 492 + " to " + interval); 493 mReleaseTime = interval; 494 if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) { 495 mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED); 496 } 497 return true; 498 } 499 } 500 return false; 501 } 502 503 /** return the release time to unbind GBA service after the request is handled */ getReleaseTime()504 public int getReleaseTime() { 505 synchronized (this) { 506 return mReleaseTime; 507 } 508 } 509 510 @VisibleForTesting getHandler()511 public Handler getHandler() { 512 return mHandler; 513 } 514 515 /** only for testing */ 516 @VisibleForTesting destroy()517 public void destroy() { 518 mHandler.removeCallbacksAndMessages(null); 519 mHandler.getLooper().quit(); 520 mRequestQueue.clear(); 521 mCallbacks.clear(); 522 unbindService(); 523 sGbaManagers.remove(mSubId); 524 } 525 logv(String msg)526 private void logv(String msg) { 527 if (DBG) { 528 Rlog.d(mLogTag, msg); 529 } 530 } 531 logd(String msg)532 private void logd(String msg) { 533 Rlog.d(mLogTag, msg); 534 } 535 loge(String msg)536 private void loge(String msg) { 537 Rlog.e(mLogTag, msg); 538 } 539 } 540