Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54c11ae1 authored by Jeff Davidson's avatar Jeff Davidson Committed by Android (Google) Code Review
Browse files

Merge "Squashed merge of master-sim into master."

parents 2391192f 277a5a2a
Loading
Loading
Loading
Loading
+22 −6
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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 */
@@ -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 {
+700 −0

File added.

Preview size limit exceeded, changes collapsed.

+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();
    }
}
+29 −63
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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.
@@ -228,7 +202,7 @@ public class UiccCarrierPrivilegeRules extends Handler {
        mStatusMessage = "Not loaded.";
        mLoadedCallback = loadedCallback;
        mRules = "";
        mAccessRules = new ArrayList<AccessRule>();
        mAccessRules = new ArrayList<>();

        openChannel();
    }
@@ -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());
                }
            }
        }
@@ -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;
            }
        }

@@ -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);
@@ -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;
            }
@@ -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);
                    }
@@ -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 {
@@ -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;
@@ -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.
     */
@@ -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 {
+1 −0
Original line number Diff line number Diff line
@@ -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