Loading src/java/com/android/internal/telephony/PhoneFactory.java +22 −6 Original line number Diff line number Diff line Loading @@ -16,17 +16,13 @@ package com.android.internal.telephony; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DEFAULT_SUBSCRIPTION; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.LocalServerSocket; import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.Rlog; Loading @@ -36,6 +32,7 @@ import android.util.LocalLog; import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.ims.ImsResolver; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneFactory; Loading Loading @@ -69,6 +66,7 @@ public class PhoneFactory { static private ProxyController sProxyController; static private UiccController sUiccController; private static @Nullable EuiccController sEuiccController; static private CommandsInterface sCommandsInterface = null; static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null; Loading Loading @@ -134,6 +132,11 @@ public class PhoneFactory { int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context); Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription); if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_EUICC)) { sEuiccController = EuiccController.init(context); } /* In case of multi SIM mode two instances of Phone, RIL are created, where as in single SIM mode only instance. isMultiSimEnabled() function checks whether it is single SIM or multi SIM mode */ Loading Loading @@ -439,6 +442,19 @@ public class PhoneFactory { pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); if (sEuiccController != null) { pw.println("EuiccController:"); pw.increaseIndent(); try { sEuiccController.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); } pw.println("SubscriptionController:"); pw.increaseIndent(); try { Loading src/java/com/android/internal/telephony/euicc/EuiccConnector.java 0 → 100644 +700 −0 File added.Preview size limit exceeded, changes collapsed. Show changes src/java/com/android/internal/telephony/euicc/EuiccController.java 0 → 100644 +282 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.euicc; import android.Manifest; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; import android.os.ServiceManager; import android.service.euicc.DownloadResult; import android.service.euicc.GetDownloadableSubscriptionMetadataResult; import android.telephony.TelephonyManager; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; /** Backing implementation of {@link android.telephony.euicc.EuiccManager}. */ public class EuiccController extends IEuiccController.Stub { private static final String TAG = "EuiccController"; private static EuiccController sInstance; private final Context mContext; private final EuiccConnector mConnector; /** Initialize the instance. Should only be called once. */ public static EuiccController init(Context context) { synchronized (EuiccController.class) { if (sInstance == null) { sInstance = new EuiccController(context); } else { Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance); } } return sInstance; } /** Get an instance. Assumes one has already been initialized with {@link #init}. */ public static EuiccController get() { if (sInstance == null) { synchronized (EuiccController.class) { if (sInstance == null) { throw new IllegalStateException("get() called before init()"); } } } return sInstance; } private EuiccController(Context context) { this(context, new EuiccConnector(context)); ServiceManager.addService("econtroller", this); } @VisibleForTesting public EuiccController(Context context, EuiccConnector connector) { mContext = context; mConnector = connector; } /** * Return the EID. * * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load, * that IPC should generally be fast, and the EID shouldn't be needed in the normal course of * operation. */ @Override public String getEid() { if (!callerCanReadPhoneStatePrivileged() && !callerHasCarrierPrivilegesForActiveSubscription()) { throw new SecurityException( "Must have carrier privileges on active subscription to read EID"); } long token = Binder.clearCallingIdentity(); try { return blockingGetEidFromEuiccService(); } finally { Binder.restoreCallingIdentity(token); } } @Override public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription, final PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata"); } long token = Binder.clearCallingIdentity(); try { final String subscriptionResultKey = EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION; mConnector.getDownloadableSubscriptionMetadata(subscription, new EuiccConnector.GetMetadataCommandCallback() { @Override public void onGetMetadataComplete( GetDownloadableSubscriptionMetadataResult result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result.result) { case GetDownloadableSubscriptionMetadataResult.RESULT_OK: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK; extrasIntent.putExtra(subscriptionResultKey, result.subscription); break; case GetDownloadableSubscriptionMetadataResult .RESULT_MUST_DEACTIVATE_REMOVABLE_SIM: resultCode = EuiccManager .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR; // TODO(b/33075886): Pass through the PendingIntent for the // resolution action. break; case GetDownloadableSubscriptionMetadataResult.RESULT_GENERIC_ERROR: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; extrasIntent.putExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, result.detailedCode); break; default: Log.wtf(TAG, "Unknown result: " + result.result); resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; break; } sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void onEuiccServiceUnavailable() { sendResult(callbackIntent, EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR, null /* extrasIntent */); } }); } finally { Binder.restoreCallingIdentity(token); } } @Override public void downloadSubscription(DownloadableSubscription subscription, boolean switchAfterDownload, final PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { // TODO(b/33075886): Allow unprivileged carriers who have carrier privileges on the // active mSubscription (if any) and the mSubscription to be downloaded. throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to download"); } long token = Binder.clearCallingIdentity(); try { mConnector.downloadSubscription(subscription, switchAfterDownload, new EuiccConnector.DownloadCommandCallback() { @Override public void onDownloadComplete(DownloadResult result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result.result) { case DownloadResult.RESULT_OK: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK; break; case DownloadResult.RESULT_MUST_DEACTIVATE_REMOVABLE_SIM: resultCode = EuiccManager .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR; // TODO(b/33075886): Pass through the PendingIntent for the // resolution action. break; case DownloadResult.RESULT_GENERIC_ERROR: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; extrasIntent.putExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, result.detailedCode); break; default: Log.wtf(TAG, "Unknown result: " + result.result); resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; break; } sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void onEuiccServiceUnavailable() { sendResult(callbackIntent, EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR, null /* extrasIntent */); } }); } finally { Binder.restoreCallingIdentity(token); } } private void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { try { callbackIntent.send(mContext, resultCode, extrasIntent); } catch (PendingIntent.CanceledException e) { // Caller canceled the callback; do nothing. } } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP"); final long token = Binder.clearCallingIdentity(); try { mConnector.dump(fd, pw, args); } finally { Binder.restoreCallingIdentity(token); } } @Nullable private String blockingGetEidFromEuiccService() { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<String> eidRef = new AtomicReference<>(); mConnector.getEid(new EuiccConnector.GetEidCommandCallback() { @Override public void onGetEidComplete(String eid) { eidRef.set(eid); latch.countDown(); } @Override public void onEuiccServiceUnavailable() { latch.countDown(); } }); try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return eidRef.get(); } private boolean callerCanReadPhoneStatePrivileged() { return mContext.checkCallingPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; } private boolean callerCanWriteEmbeddedSubscriptions() { return mContext.checkCallingPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) == PackageManager.PERMISSION_GRANTED; } /** * Returns whether the caller has carrier privileges for the active mSubscription on this eUICC. */ private boolean callerHasCarrierPrivilegesForActiveSubscription() { // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); return tm.hasCarrierPrivileges(); } } src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java +29 −63 Original line number Diff line number Diff line Loading @@ -28,16 +28,14 @@ import android.os.Handler; import android.os.Message; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.telephony.UiccAccessRule; import android.text.TextUtils; import com.android.internal.telephony.CommandException; import java.io.FileDescriptor; import java.io.PrintWriter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -109,30 +107,6 @@ public class UiccCarrierPrivilegeRules extends Handler { private static final int MAX_RETRY = 1; private static final int RETRY_INTERVAL_MS = 10000; // Describes a single rule. private static class AccessRule { public byte[] certificateHash; public String packageName; public long accessType; // This bit is not currently used, but reserved for future use. AccessRule(byte[] certificateHash, String packageName, long accessType) { this.certificateHash = certificateHash; this.packageName = packageName; this.accessType = accessType; } boolean matches(byte[] certHash, String packageName) { return certHash != null && Arrays.equals(this.certificateHash, certHash) && (TextUtils.isEmpty(this.packageName) || this.packageName.equals(packageName)); } @Override public String toString() { return "cert: " + IccUtils.bytesToHexString(certificateHash) + " pkg: " + packageName + " access: " + accessType; } } // Used for parsing the data from the UICC. public static class TLV { private static final int SINGLE_BYTE_MAX_LENGTH = 0x80; Loading Loading @@ -202,7 +176,7 @@ public class UiccCarrierPrivilegeRules extends Handler { private UiccCard mUiccCard; // Parent private UiccPkcs15 mUiccPkcs15; // ARF fallback private AtomicInteger mState; private List<AccessRule> mAccessRules; private List<UiccAccessRule> mAccessRules; private String mRules; private Message mLoadedCallback; private String mStatusMessage; // Only used for debugging. Loading @@ -228,7 +202,7 @@ public class UiccCarrierPrivilegeRules extends Handler { mStatusMessage = "Not loaded."; mLoadedCallback = loadedCallback; mRules = ""; mAccessRules = new ArrayList<AccessRule>(); mAccessRules = new ArrayList<>(); openChannel(); } Loading @@ -255,9 +229,9 @@ public class UiccCarrierPrivilegeRules extends Handler { public List<String> getPackageNames() { List<String> pkgNames = new ArrayList<String>(); if (mAccessRules != null) { for (AccessRule ar : mAccessRules) { if(!TextUtils.isEmpty(ar.packageName)) { pkgNames.add(ar.packageName); for (UiccAccessRule ar : mAccessRules) { if (!TextUtils.isEmpty(ar.getPackageName())) { pkgNames.add(ar.getPackageName()); } } } Loading @@ -279,12 +253,10 @@ public class UiccCarrierPrivilegeRules extends Handler { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES; } // SHA-1 is for backward compatible support only, strongly discouraged for new use. byte[] certHash = getCertHash(signature, "SHA-1"); byte[] certHash256 = getCertHash(signature, "SHA-256"); for (AccessRule ar : mAccessRules) { if (ar.matches(certHash, packageName) || ar.matches(certHash256, packageName)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; for (UiccAccessRule ar : mAccessRules) { int accessStatus = ar.getCarrierPrivilegeStatus(signature, packageName); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; } } Loading Loading @@ -315,7 +287,8 @@ public class UiccCarrierPrivilegeRules extends Handler { // is disabled by default, and some other component wants to enable it when it has // gained carrier privileges (as an indication that a matching SIM has been inserted). PackageInfo pInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); PackageManager.GET_SIGNATURES | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS); return getCarrierPrivilegeStatus(pInfo); } catch (PackageManager.NameNotFoundException ex) { Rlog.e(LOG_TAG, "NameNotFoundException", ex); Loading @@ -330,9 +303,15 @@ public class UiccCarrierPrivilegeRules extends Handler { * @return Access status. */ public int getCarrierPrivilegeStatus(PackageInfo packageInfo) { Signature[] signatures = packageInfo.signatures; for (Signature sig : signatures) { int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName); int state = mState.get(); if (state == STATE_LOADING) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (state == STATE_ERROR) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES; } for (UiccAccessRule ar : mAccessRules) { int accessStatus = ar.getCarrierPrivilegeStatus(packageInfo); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; } Loading Loading @@ -486,7 +465,7 @@ public class UiccCarrierPrivilegeRules extends Handler { updateState(STATE_ERROR, "No ARA or ARF."); } else { for (String cert : mUiccPkcs15.getRules()) { AccessRule accessRule = new AccessRule( UiccAccessRule accessRule = new UiccAccessRule( IccUtils.hexStringToBytes(cert), "", 0x00); mAccessRules.add(accessRule); } Loading Loading @@ -525,18 +504,18 @@ public class UiccCarrierPrivilegeRules extends Handler { /* * Parses the rules from the input string. */ private static List<AccessRule> parseRules(String rules) { private static List<UiccAccessRule> parseRules(String rules) { log("Got rules: " + rules); TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40 allRefArDo.parse(rules, true); String arDos = allRefArDo.value; List<AccessRule> accessRules = new ArrayList<AccessRule>(); List<UiccAccessRule> accessRules = new ArrayList<>(); while (!arDos.isEmpty()) { TLV refArDo = new TLV(TAG_REF_AR_DO); //E2 arDos = refArDo.parse(arDos, false); AccessRule accessRule = parseRefArdo(refArDo.value); UiccAccessRule accessRule = parseRefArdo(refArDo.value); if (accessRule != null) { accessRules.add(accessRule); } else { Loading @@ -549,7 +528,7 @@ public class UiccCarrierPrivilegeRules extends Handler { /* * Parses a single rule. */ private static AccessRule parseRefArdo(String rule) { private static UiccAccessRule parseRefArdo(String rule) { log("Got rule: " + rule); String certificateHash = null; Loading Loading @@ -598,24 +577,11 @@ public class UiccCarrierPrivilegeRules extends Handler { } } AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash), packageName, accessType); UiccAccessRule accessRule = new UiccAccessRule( IccUtils.hexStringToBytes(certificateHash), packageName, accessType); return accessRule; } /* * Converts a Signature into a Certificate hash usable for comparison. */ private static byte[] getCertHash(Signature signature, String algo) { try { MessageDigest md = MessageDigest.getInstance(algo); return md.digest(signature.toByteArray()); } catch (NoSuchAlgorithmException ex) { Rlog.e(LOG_TAG, "NoSuchAlgorithmException: " + ex); } return null; } /* * Updates the state and notifies the UiccCard that the rules have finished loading. */ Loading @@ -641,7 +607,7 @@ public class UiccCarrierPrivilegeRules extends Handler { pw.println(" mStatusMessage='" + mStatusMessage + "'"); if (mAccessRules != null) { pw.println(" mAccessRules: "); for (AccessRule ar : mAccessRules) { for (UiccAccessRule ar : mAccessRules) { pw.println(" rule='" + ar + "'"); } } else { Loading tests/telephonytests/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core LOCAL_STATIC_JAVA_LIBRARIES := guava \ frameworks-base-testutils \ mockito-target-minus-junit4 \ android-support-test \ platform-test-annotations \ Loading Loading
src/java/com/android/internal/telephony/PhoneFactory.java +22 −6 Original line number Diff line number Diff line Loading @@ -16,17 +16,13 @@ package com.android.internal.telephony; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DEFAULT_SUBSCRIPTION; import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.LocalServerSocket; import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.telephony.Rlog; Loading @@ -36,6 +32,7 @@ import android.util.LocalLog; import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager; import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.ims.ImsResolver; import com.android.internal.telephony.imsphone.ImsPhone; import com.android.internal.telephony.imsphone.ImsPhoneFactory; Loading Loading @@ -69,6 +66,7 @@ public class PhoneFactory { static private ProxyController sProxyController; static private UiccController sUiccController; private static @Nullable EuiccController sEuiccController; static private CommandsInterface sCommandsInterface = null; static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null; Loading Loading @@ -134,6 +132,11 @@ public class PhoneFactory { int cdmaSubscription = CdmaSubscriptionSourceManager.getDefault(context); Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription); if (context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_EUICC)) { sEuiccController = EuiccController.init(context); } /* In case of multi SIM mode two instances of Phone, RIL are created, where as in single SIM mode only instance. isMultiSimEnabled() function checks whether it is single SIM or multi SIM mode */ Loading Loading @@ -439,6 +442,19 @@ public class PhoneFactory { pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); if (sEuiccController != null) { pw.println("EuiccController:"); pw.increaseIndent(); try { sEuiccController.dump(fd, pw, args); } catch (Exception e) { e.printStackTrace(); } pw.flush(); pw.decreaseIndent(); pw.println("++++++++++++++++++++++++++++++++"); } pw.println("SubscriptionController:"); pw.increaseIndent(); try { Loading
src/java/com/android/internal/telephony/euicc/EuiccConnector.java 0 → 100644 +700 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
src/java/com/android/internal/telephony/euicc/EuiccController.java 0 → 100644 +282 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony.euicc; import android.Manifest; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Binder; import android.os.ServiceManager; import android.service.euicc.DownloadResult; import android.service.euicc.GetDownloadableSubscriptionMetadataResult; import android.telephony.TelephonyManager; import android.telephony.euicc.DownloadableSubscription; import android.telephony.euicc.EuiccManager; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; /** Backing implementation of {@link android.telephony.euicc.EuiccManager}. */ public class EuiccController extends IEuiccController.Stub { private static final String TAG = "EuiccController"; private static EuiccController sInstance; private final Context mContext; private final EuiccConnector mConnector; /** Initialize the instance. Should only be called once. */ public static EuiccController init(Context context) { synchronized (EuiccController.class) { if (sInstance == null) { sInstance = new EuiccController(context); } else { Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance); } } return sInstance; } /** Get an instance. Assumes one has already been initialized with {@link #init}. */ public static EuiccController get() { if (sInstance == null) { synchronized (EuiccController.class) { if (sInstance == null) { throw new IllegalStateException("get() called before init()"); } } } return sInstance; } private EuiccController(Context context) { this(context, new EuiccConnector(context)); ServiceManager.addService("econtroller", this); } @VisibleForTesting public EuiccController(Context context, EuiccConnector connector) { mContext = context; mConnector = connector; } /** * Return the EID. * * <p>For API simplicity, this call blocks until completion; while it requires an IPC to load, * that IPC should generally be fast, and the EID shouldn't be needed in the normal course of * operation. */ @Override public String getEid() { if (!callerCanReadPhoneStatePrivileged() && !callerHasCarrierPrivilegesForActiveSubscription()) { throw new SecurityException( "Must have carrier privileges on active subscription to read EID"); } long token = Binder.clearCallingIdentity(); try { return blockingGetEidFromEuiccService(); } finally { Binder.restoreCallingIdentity(token); } } @Override public void getDownloadableSubscriptionMetadata(DownloadableSubscription subscription, final PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to get metadata"); } long token = Binder.clearCallingIdentity(); try { final String subscriptionResultKey = EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION; mConnector.getDownloadableSubscriptionMetadata(subscription, new EuiccConnector.GetMetadataCommandCallback() { @Override public void onGetMetadataComplete( GetDownloadableSubscriptionMetadataResult result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result.result) { case GetDownloadableSubscriptionMetadataResult.RESULT_OK: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK; extrasIntent.putExtra(subscriptionResultKey, result.subscription); break; case GetDownloadableSubscriptionMetadataResult .RESULT_MUST_DEACTIVATE_REMOVABLE_SIM: resultCode = EuiccManager .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR; // TODO(b/33075886): Pass through the PendingIntent for the // resolution action. break; case GetDownloadableSubscriptionMetadataResult.RESULT_GENERIC_ERROR: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; extrasIntent.putExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, result.detailedCode); break; default: Log.wtf(TAG, "Unknown result: " + result.result); resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; break; } sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void onEuiccServiceUnavailable() { sendResult(callbackIntent, EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR, null /* extrasIntent */); } }); } finally { Binder.restoreCallingIdentity(token); } } @Override public void downloadSubscription(DownloadableSubscription subscription, boolean switchAfterDownload, final PendingIntent callbackIntent) { if (!callerCanWriteEmbeddedSubscriptions()) { // TODO(b/33075886): Allow unprivileged carriers who have carrier privileges on the // active mSubscription (if any) and the mSubscription to be downloaded. throw new SecurityException("Must have WRITE_EMBEDDED_SUBSCRIPTIONS to download"); } long token = Binder.clearCallingIdentity(); try { mConnector.downloadSubscription(subscription, switchAfterDownload, new EuiccConnector.DownloadCommandCallback() { @Override public void onDownloadComplete(DownloadResult result) { Intent extrasIntent = new Intent(); final int resultCode; switch (result.result) { case DownloadResult.RESULT_OK: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK; break; case DownloadResult.RESULT_MUST_DEACTIVATE_REMOVABLE_SIM: resultCode = EuiccManager .EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR; // TODO(b/33075886): Pass through the PendingIntent for the // resolution action. break; case DownloadResult.RESULT_GENERIC_ERROR: resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; extrasIntent.putExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, result.detailedCode); break; default: Log.wtf(TAG, "Unknown result: " + result.result); resultCode = EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR; break; } sendResult(callbackIntent, resultCode, extrasIntent); } @Override public void onEuiccServiceUnavailable() { sendResult(callbackIntent, EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_GENERIC_ERROR, null /* extrasIntent */); } }); } finally { Binder.restoreCallingIdentity(token); } } private void sendResult(PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { try { callbackIntent.send(mContext, resultCode, extrasIntent); } catch (PendingIntent.CanceledException e) { // Caller canceled the callback; do nothing. } } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP"); final long token = Binder.clearCallingIdentity(); try { mConnector.dump(fd, pw, args); } finally { Binder.restoreCallingIdentity(token); } } @Nullable private String blockingGetEidFromEuiccService() { final CountDownLatch latch = new CountDownLatch(1); final AtomicReference<String> eidRef = new AtomicReference<>(); mConnector.getEid(new EuiccConnector.GetEidCommandCallback() { @Override public void onGetEidComplete(String eid) { eidRef.set(eid); latch.countDown(); } @Override public void onEuiccServiceUnavailable() { latch.countDown(); } }); try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return eidRef.get(); } private boolean callerCanReadPhoneStatePrivileged() { return mContext.checkCallingPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; } private boolean callerCanWriteEmbeddedSubscriptions() { return mContext.checkCallingPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) == PackageManager.PERMISSION_GRANTED; } /** * Returns whether the caller has carrier privileges for the active mSubscription on this eUICC. */ private boolean callerHasCarrierPrivilegesForActiveSubscription() { // TODO(b/36260308): We should plumb a slot ID through here for multi-SIM devices. TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); return tm.hasCarrierPrivileges(); } }
src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java +29 −63 Original line number Diff line number Diff line Loading @@ -28,16 +28,14 @@ import android.os.Handler; import android.os.Message; import android.telephony.Rlog; import android.telephony.TelephonyManager; import android.telephony.UiccAccessRule; import android.text.TextUtils; import com.android.internal.telephony.CommandException; import java.io.FileDescriptor; import java.io.PrintWriter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; Loading Loading @@ -109,30 +107,6 @@ public class UiccCarrierPrivilegeRules extends Handler { private static final int MAX_RETRY = 1; private static final int RETRY_INTERVAL_MS = 10000; // Describes a single rule. private static class AccessRule { public byte[] certificateHash; public String packageName; public long accessType; // This bit is not currently used, but reserved for future use. AccessRule(byte[] certificateHash, String packageName, long accessType) { this.certificateHash = certificateHash; this.packageName = packageName; this.accessType = accessType; } boolean matches(byte[] certHash, String packageName) { return certHash != null && Arrays.equals(this.certificateHash, certHash) && (TextUtils.isEmpty(this.packageName) || this.packageName.equals(packageName)); } @Override public String toString() { return "cert: " + IccUtils.bytesToHexString(certificateHash) + " pkg: " + packageName + " access: " + accessType; } } // Used for parsing the data from the UICC. public static class TLV { private static final int SINGLE_BYTE_MAX_LENGTH = 0x80; Loading Loading @@ -202,7 +176,7 @@ public class UiccCarrierPrivilegeRules extends Handler { private UiccCard mUiccCard; // Parent private UiccPkcs15 mUiccPkcs15; // ARF fallback private AtomicInteger mState; private List<AccessRule> mAccessRules; private List<UiccAccessRule> mAccessRules; private String mRules; private Message mLoadedCallback; private String mStatusMessage; // Only used for debugging. Loading @@ -228,7 +202,7 @@ public class UiccCarrierPrivilegeRules extends Handler { mStatusMessage = "Not loaded."; mLoadedCallback = loadedCallback; mRules = ""; mAccessRules = new ArrayList<AccessRule>(); mAccessRules = new ArrayList<>(); openChannel(); } Loading @@ -255,9 +229,9 @@ public class UiccCarrierPrivilegeRules extends Handler { public List<String> getPackageNames() { List<String> pkgNames = new ArrayList<String>(); if (mAccessRules != null) { for (AccessRule ar : mAccessRules) { if(!TextUtils.isEmpty(ar.packageName)) { pkgNames.add(ar.packageName); for (UiccAccessRule ar : mAccessRules) { if (!TextUtils.isEmpty(ar.getPackageName())) { pkgNames.add(ar.getPackageName()); } } } Loading @@ -279,12 +253,10 @@ public class UiccCarrierPrivilegeRules extends Handler { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES; } // SHA-1 is for backward compatible support only, strongly discouraged for new use. byte[] certHash = getCertHash(signature, "SHA-1"); byte[] certHash256 = getCertHash(signature, "SHA-256"); for (AccessRule ar : mAccessRules) { if (ar.matches(certHash, packageName) || ar.matches(certHash256, packageName)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; for (UiccAccessRule ar : mAccessRules) { int accessStatus = ar.getCarrierPrivilegeStatus(signature, packageName); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; } } Loading Loading @@ -315,7 +287,8 @@ public class UiccCarrierPrivilegeRules extends Handler { // is disabled by default, and some other component wants to enable it when it has // gained carrier privileges (as an indication that a matching SIM has been inserted). PackageInfo pInfo = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); PackageManager.GET_SIGNATURES | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS); return getCarrierPrivilegeStatus(pInfo); } catch (PackageManager.NameNotFoundException ex) { Rlog.e(LOG_TAG, "NameNotFoundException", ex); Loading @@ -330,9 +303,15 @@ public class UiccCarrierPrivilegeRules extends Handler { * @return Access status. */ public int getCarrierPrivilegeStatus(PackageInfo packageInfo) { Signature[] signatures = packageInfo.signatures; for (Signature sig : signatures) { int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName); int state = mState.get(); if (state == STATE_LOADING) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED; } else if (state == STATE_ERROR) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES; } for (UiccAccessRule ar : mAccessRules) { int accessStatus = ar.getCarrierPrivilegeStatus(packageInfo); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; } Loading Loading @@ -486,7 +465,7 @@ public class UiccCarrierPrivilegeRules extends Handler { updateState(STATE_ERROR, "No ARA or ARF."); } else { for (String cert : mUiccPkcs15.getRules()) { AccessRule accessRule = new AccessRule( UiccAccessRule accessRule = new UiccAccessRule( IccUtils.hexStringToBytes(cert), "", 0x00); mAccessRules.add(accessRule); } Loading Loading @@ -525,18 +504,18 @@ public class UiccCarrierPrivilegeRules extends Handler { /* * Parses the rules from the input string. */ private static List<AccessRule> parseRules(String rules) { private static List<UiccAccessRule> parseRules(String rules) { log("Got rules: " + rules); TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40 allRefArDo.parse(rules, true); String arDos = allRefArDo.value; List<AccessRule> accessRules = new ArrayList<AccessRule>(); List<UiccAccessRule> accessRules = new ArrayList<>(); while (!arDos.isEmpty()) { TLV refArDo = new TLV(TAG_REF_AR_DO); //E2 arDos = refArDo.parse(arDos, false); AccessRule accessRule = parseRefArdo(refArDo.value); UiccAccessRule accessRule = parseRefArdo(refArDo.value); if (accessRule != null) { accessRules.add(accessRule); } else { Loading @@ -549,7 +528,7 @@ public class UiccCarrierPrivilegeRules extends Handler { /* * Parses a single rule. */ private static AccessRule parseRefArdo(String rule) { private static UiccAccessRule parseRefArdo(String rule) { log("Got rule: " + rule); String certificateHash = null; Loading Loading @@ -598,24 +577,11 @@ public class UiccCarrierPrivilegeRules extends Handler { } } AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash), packageName, accessType); UiccAccessRule accessRule = new UiccAccessRule( IccUtils.hexStringToBytes(certificateHash), packageName, accessType); return accessRule; } /* * Converts a Signature into a Certificate hash usable for comparison. */ private static byte[] getCertHash(Signature signature, String algo) { try { MessageDigest md = MessageDigest.getInstance(algo); return md.digest(signature.toByteArray()); } catch (NoSuchAlgorithmException ex) { Rlog.e(LOG_TAG, "NoSuchAlgorithmException: " + ex); } return null; } /* * Updates the state and notifies the UiccCard that the rules have finished loading. */ Loading @@ -641,7 +607,7 @@ public class UiccCarrierPrivilegeRules extends Handler { pw.println(" mStatusMessage='" + mStatusMessage + "'"); if (mAccessRules != null) { pw.println(" mAccessRules: "); for (AccessRule ar : mAccessRules) { for (UiccAccessRule ar : mAccessRules) { pw.println(" rule='" + ar + "'"); } } else { Loading
tests/telephonytests/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core LOCAL_STATIC_JAVA_LIBRARIES := guava \ frameworks-base-testutils \ mockito-target-minus-junit4 \ android-support-test \ platform-test-annotations \ Loading