1 /*
2  * Copyright (C) 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 android.telephony.gba;
18 
19 import android.annotation.NonNull;
20 import android.annotation.SystemApi;
21 import android.app.Service;
22 import android.content.Intent;
23 import android.net.Uri;
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.telephony.Annotation.UiccAppTypeExt;
32 import android.telephony.IBootstrapAuthenticationCallback;
33 import android.telephony.TelephonyManager;
34 import android.telephony.TelephonyManager.AuthenticationFailureReason;
35 import android.util.Log;
36 import android.util.SparseArray;
37 
38 /**
39   * Base class for GBA Service. Any implementation which wants to provide
40   * GBA service must extend this class.
41   *
42   * <p>Note that the application to implement the service must declare to use
43   * the permission {@link android.Manifest.permission#BIND_GBA_SERVICE},
44   * and filter the intent of {@link #SERVICE_INTERFACE}.
45   * The manifest of the service must follow the format below:
46   *
47   * <p>...
48   * <service
49   *     android:name=".EgGbaService"
50   *     android:directBootAware="true"
51   *     android:permission="android.permission.BIND_GBA_SERVICE" >
52   *     ...
53   *     <intent-filter>
54   *         <action android:name="android.telephony.gba.GbaService"/>
55   *     </intent-filter>
56   * </service>
57   * ...
58   *
59   * <p>The service should also be file-based encryption (FBE) aware.
60   * {@hide}
61   */
62 @SystemApi
63 public class GbaService extends Service  {
64     private static final boolean DBG = Build.IS_DEBUGGABLE;
65     private static final String TAG = "GbaService";
66 
67     /**
68     * The intent must be defined as an intent-filter in the
69     * AndroidManifest of the GbaService.
70     */
71     public static final String SERVICE_INTERFACE = "android.telephony.gba.GbaService";
72 
73     private static final int EVENT_GBA_AUTH_REQUEST = 1;
74 
75     private final HandlerThread mHandlerThread;
76     private final GbaServiceHandler mHandler;
77 
78     private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>();
79     private final IGbaServiceWrapper mBinder = new IGbaServiceWrapper();
80 
81     /**
82      * Default constructor.
83      */
GbaService()84     public GbaService() {
85         mHandlerThread = new HandlerThread(TAG);
86         mHandlerThread.start();
87 
88         mHandler = new GbaServiceHandler(mHandlerThread.getLooper());
89         Log.d(TAG, "GBA service created");
90     }
91 
92     private class GbaServiceHandler extends Handler {
93 
GbaServiceHandler(Looper looper)94         GbaServiceHandler(Looper looper) {
95             super(looper);
96         }
97 
98         @Override
handleMessage(Message msg)99         public void handleMessage(Message msg) {
100             switch (msg.what) {
101                 case EVENT_GBA_AUTH_REQUEST:
102                     GbaAuthRequest req = (GbaAuthRequest) msg.obj;
103                     synchronized (mCallbacks) {
104                         mCallbacks.put(req.getToken(), req.getCallback());
105                     }
106                     onAuthenticationRequest(req.getSubId(), req.getToken(), req.getAppType(),
107                             req.getNafUrl(), req.getSecurityProtocol(), req.isForceBootStrapping());
108                     break;
109                 default:
110                     break;
111             }
112         }
113     }
114 
115     /**
116      * Called by the platform when a GBA authentication request is received from
117      * {@link TelephonyManager#bootstrapAuthenticationRequest} to get the KsNAF for
118      * a particular NAF.
119      *
120      * @param subscriptionId the ICC card to be used for the bootstrapping authentication.
121      * @param token the identification of the authentication request.
122      * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
123      * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
124      * @param nafUrl Network Application Function(NAF) fully qualified domain name and
125      * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
126      * part is the constant string "3GPP-bootstrapping" (GBA_ME),
127      * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
128      * and the latter part shall be the FQDN of the NAF (e.g.
129      * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
130      * or "3GPP-bootstrapping-digest@naf1.operator.com").
131      * @param securityProtocol Security protocol identifier between UE and NAF.  See
132      * 3GPP TS 33.220 Annex H. Application can use
133      * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
134      * {@link UaSecurityProtocolIdentifier#create3GppUaSpId},
135      * to create the ua security protocol identifier as needed
136      * @param forceBootStrapping true=force bootstrapping, false=do not force
137      * bootstrapping. Bootstrapping shouldn't be forced unless the application sees
138      * authentication errors from the server.
139      * Response is returned via {@link TelephonyManager#BootstrapAuthenticationCallback}
140      * along with the token to identify the request.
141      *
142      * <p>Note that this is not called in the main thread.
143      */
onAuthenticationRequest(int subscriptionId, int token, @UiccAppTypeExt int appType, @NonNull Uri nafUrl, @NonNull byte[] securityProtocol, boolean forceBootStrapping)144     public void onAuthenticationRequest(int subscriptionId, int token, @UiccAppTypeExt int appType,
145             @NonNull Uri nafUrl, @NonNull byte[] securityProtocol, boolean forceBootStrapping) {
146         //Default implementation should be overridden by vendor Gba Service. Vendor Gba Service
147         //should handle the gba bootstrap authentication request, and call reportKeysAvailable or
148         //reportAuthenticationFailure to notify the caller accordingly.
149         reportAuthenticationFailure(
150                 token, TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED);
151     }
152 
153     /**
154      * Called by {@link GbaService} when the previously requested GBA keys are available
155      * (@see onAuthenticationRequest())
156      *
157      * @param token unique identifier of the request.
158      * @param gbaKey KsNaf Response.
159      * @param transactionId Bootstrapping Transaction ID.
160      * @throws RuntimeException when there is remote failure of callback.
161      */
reportKeysAvailable(int token, @NonNull byte[] gbaKey, @NonNull String transactionId)162     public final void reportKeysAvailable(int token, @NonNull byte[] gbaKey,
163             @NonNull String transactionId) throws RuntimeException {
164         IBootstrapAuthenticationCallback cb = null;
165         synchronized (mCallbacks) {
166             cb = mCallbacks.get(token);
167             mCallbacks.remove(token);
168         }
169         if (cb != null) {
170             try {
171                 cb.onKeysAvailable(token, gbaKey, transactionId);
172             } catch (RemoteException exception) {
173                 throw exception.rethrowAsRuntimeException();
174             }
175         }
176     }
177 
178     /**
179      * Invoked when the previously requested GBA key authentication failed
180      * (@see onAuthenticationRequest())
181      *
182      * @param token unique identifier of the request.
183      * @param reason The reason for the authentication failure.
184      * @throws RuntimeException when there is remote failure of callback.
185      */
reportAuthenticationFailure(int token, @AuthenticationFailureReason int reason)186     public final void reportAuthenticationFailure(int token,
187             @AuthenticationFailureReason int reason) throws RuntimeException {
188         IBootstrapAuthenticationCallback cb = null;
189         synchronized (mCallbacks) {
190             cb = mCallbacks.get(token);
191             mCallbacks.remove(token);
192         }
193         if (cb != null) {
194             try {
195                 cb.onAuthenticationFailure(token, reason);
196             } catch (RemoteException exception) {
197                 throw exception.rethrowAsRuntimeException();
198             }
199         }
200     }
201 
202     /** @hide */
203     @Override
onBind(Intent intent)204     public IBinder onBind(Intent intent) {
205         if (SERVICE_INTERFACE.equals(intent.getAction())) {
206             Log.d(TAG, "GbaService Bound.");
207             return mBinder;
208         }
209         return null;
210     }
211 
212     /** @hide */
213     @Override
onDestroy()214     public void onDestroy() {
215         mHandlerThread.quit();
216         super.onDestroy();
217     }
218 
219     private class IGbaServiceWrapper extends IGbaService.Stub {
220         @Override
authenticationRequest(GbaAuthRequest request)221         public void authenticationRequest(GbaAuthRequest request) {
222             if (DBG) Log.d(TAG, "receive request: " + request);
223             mHandler.obtainMessage(EVENT_GBA_AUTH_REQUEST, request).sendToTarget();
224         }
225     }
226 }
227