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