/*
 * 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 android.telephony;

import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

import com.android.internal.telephony.uicc.IccUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;

Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control specification.
@hide
/** * Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control * specification. * * @hide */
@SystemApi public final class UiccAccessRule implements Parcelable { private static final String TAG = "UiccAccessRule"; private static final int ENCODING_VERSION = 1; public static final Creator<UiccAccessRule> CREATOR = new Creator<UiccAccessRule>() { @Override public UiccAccessRule createFromParcel(Parcel in) { return new UiccAccessRule(in); } @Override public UiccAccessRule[] newArray(int size) { return new UiccAccessRule[size]; } };
Encode these access rules as a byte array which can be parsed with decodeRules.
@hide
/** * Encode these access rules as a byte array which can be parsed with {@link #decodeRules}. * @hide */
@Nullable public static byte[] encodeRules(@Nullable UiccAccessRule[] accessRules) { if (accessRules == null) { return null; } try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream output = new DataOutputStream(baos); output.writeInt(ENCODING_VERSION); output.writeInt(accessRules.length); for (UiccAccessRule accessRule : accessRules) { output.writeInt(accessRule.mCertificateHash.length); output.write(accessRule.mCertificateHash); if (accessRule.mPackageName != null) { output.writeBoolean(true); output.writeUTF(accessRule.mPackageName); } else { output.writeBoolean(false); } output.writeLong(accessRule.mAccessType); } output.close(); return baos.toByteArray(); } catch (IOException e) { throw new IllegalStateException( "ByteArrayOutputStream should never lead to an IOException", e); } }
Decodes a byte array generated with encodeRules.
@hide
/** * Decodes a byte array generated with {@link #encodeRules}. * @hide */
@Nullable public static UiccAccessRule[] decodeRules(@Nullable byte[] encodedRules) { if (encodedRules == null) { return null; } ByteArrayInputStream bais = new ByteArrayInputStream(encodedRules); try (DataInputStream input = new DataInputStream(bais)) { input.readInt(); // version; currently ignored int count = input.readInt(); UiccAccessRule[] accessRules = new UiccAccessRule[count]; for (int i = 0; i < count; i++) { int certificateHashLength = input.readInt(); byte[] certificateHash = new byte[certificateHashLength]; input.readFully(certificateHash); String packageName = input.readBoolean() ? input.readUTF() : null; long accessType = input.readLong(); accessRules[i] = new UiccAccessRule(certificateHash, packageName, accessType); } input.close(); return accessRules; } catch (IOException e) { throw new IllegalStateException( "ByteArrayInputStream should never lead to an IOException", e); } } private final byte[] mCertificateHash; private final @Nullable String mPackageName; // This bit is not currently used, but reserved for future use. private final long mAccessType; public UiccAccessRule(byte[] certificateHash, @Nullable String packageName, long accessType) { this.mCertificateHash = certificateHash; this.mPackageName = packageName; this.mAccessType = accessType; } UiccAccessRule(Parcel in) { mCertificateHash = in.createByteArray(); mPackageName = in.readString(); mAccessType = in.readLong(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByteArray(mCertificateHash); dest.writeString(mPackageName); dest.writeLong(mAccessType); }
Return the package name this rule applies to.
Returns:the package name, or null if this rule applies to any package signed with the given certificate.
/** * Return the package name this rule applies to. * * @return the package name, or null if this rule applies to any package signed with the given * certificate. */
public @Nullable String getPackageName() { return mPackageName; }
Returns the hex string of the certificate hash.
/** * Returns the hex string of the certificate hash. */
public String getCertificateHexString() { return IccUtils.bytesToHexString(mCertificateHash); }
Returns the carrier privilege status associated with the given package.
Params:
Returns:either TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS or TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS.
/** * Returns the carrier privilege status associated with the given package. * * @param packageInfo package info fetched from * {@link android.content.pm.PackageManager#getPackageInfo}. * {@link android.content.pm.PackageManager#GET_SIGNATURES} must have been passed in. * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}. */
public int getCarrierPrivilegeStatus(PackageInfo packageInfo) { if (packageInfo.signatures == null || packageInfo.signatures.length == 0) { throw new IllegalArgumentException( "Must use GET_SIGNATURES when looking up package info"); } for (Signature sig : packageInfo.signatures) { int accessStatus = getCarrierPrivilegeStatus(sig, packageInfo.packageName); if (accessStatus != TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS) { return accessStatus; } } return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; }
Returns the carrier privilege status for the given certificate and package name.
Params:
  • signature – The signature of the certificate.
  • packageName – name of the package.
Returns:either TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS or TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS.
/** * Returns the carrier privilege status for the given certificate and package name. * * @param signature The signature of the certificate. * @param packageName name of the package. * @return either {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_HAS_ACCESS} or * {@link TelephonyManager#CARRIER_PRIVILEGE_STATUS_NO_ACCESS}. */
public int getCarrierPrivilegeStatus(Signature signature, String packageName) { // 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"); if (matches(certHash, packageName) || matches(certHash256, packageName)) { return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; } return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; } private boolean matches(byte[] certHash, String packageName) { return certHash != null && Arrays.equals(this.mCertificateHash, certHash) && (TextUtils.isEmpty(this.mPackageName) || this.mPackageName.equals(packageName)); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } UiccAccessRule that = (UiccAccessRule) obj; return Arrays.equals(mCertificateHash, that.mCertificateHash) && Objects.equals(mPackageName, that.mPackageName) && mAccessType == that.mAccessType; } @Override public int hashCode() { int result = 1; result = 31 * result + Arrays.hashCode(mCertificateHash); result = 31 * result + Objects.hashCode(mPackageName); result = 31 * result + Objects.hashCode(mAccessType); return result; } @Override public String toString() { return "cert: " + IccUtils.bytesToHexString(mCertificateHash) + " pkg: " + mPackageName + " access: " + mAccessType; } @Override public int describeContents() { return 0; }
Converts a Signature into a Certificate hash usable for comparison.
/** * 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(TAG, "NoSuchAlgorithmException: " + ex); } return null; } }