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