Files
frameworks_base/telephony/java/android/telephony/UiccAccessRule.java
Jeff Davidson 35cda39422 Squashed merge of master-sim into master.
Includes the following commits:

==

New system feature: eUICC.

Presence of this feature implies that eUICC-related APIs are expected
to function as long as an eUICC is present in the device. Note that an
eUICC may be embedded in the device but may also be removable.

==

Add empty EuiccManager API and plumbing.

==

Add stub EuiccService.

EuiccService is the class that the LPA app must implement; for now,
just define the action and priority so that the implementation can be
found. Actual methods will come later.

Also declare two relevant permissions: BIND_EUICC_SERVICE, which the
implementation must require (so that nobody else can bind to the
service directly), and WRITE_EMBEDDED_SUBSCRIPTIONS, which permits
signature|privileged apps and CTS (via development) to access
EuiccManager APIs.

==

Add UiccAccessRule based off UiccCarrierPrivilegeRules#AccessRule.

This class can be used to transfer access rules between an
EuiccService implementation and the platform.

We also add a simple encoding/decoding of a list of rules so that they
may be stored in the subscription info table.

==

Add getEid() to EuiccManager/EuiccService.

getEid() fetches the EID. It requires either a privileged permission
(READ_PRIVILEGED_PHONE_STATE) or carrier privileges on the
currently-active profile. Until there is a use case that requires
opening this up to apps with only READ_PHONE_STATE, we shouldn't do
so.

To avoid churn in the future, the API signatures for EuiccService
include a slot ID to identify which SIM slot is being used. However,
this parameter is currently not populated correctly (nor is it usable,
as no Telephony APIs accept a slot ID to address commands). There is
no need to expose it yet in the EuiccManager APIs as we expect to
follow the TelephonyManager pattern of allowing per-slot instances of
EuiccManager in the future while keeping other method signatures the
same.

==

Define Euicc UI actions in EuiccManager/EuiccService.

The EuiccManager actions are to be implemented by the platform (and
only the platform), which forwards the actions to the active
implementation.

Also, remove our explicit priority meta-data tag as we can just rely
on android:priority in the corresponding intent-filter.

==

APIs for downloading embedded subscriptions.

Includes:

-getDownloadableSubscriptionMetadata, used by the platform and LUI to
fetch metadata about a downloadable subscription. The platform will
use this to perform the necessary permission checks (only allowing
otherwise-unprivileged apps to download profiles that are permitted
per the subscription metadata), and the LUI can use this to present
the name of the profile.

-downloadSubscription, to actually perform a profile download.

The stub for startResolutionActivity is included but not implemented;
resolution activities will be handled in a follow-up change.

==

Test: TreeHugger
Change-Id: I47b1da5a69f0736012cb137e02cd6c4e07fdaace
2017-04-03 11:17:53 -07:00

219 lines
8.0 KiB
Java

/*
* 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;
/**
* Describes a single UICC access rule according to the GlobalPlatform Secure Element Access Control
* specification.
*
* @hide
*
* TODO(b/35851809): Make this a 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 {@link #decodeRules}.
* @hide
*/
public static byte[] encodeRules(UiccAccessRule[] accessRules) {
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);
output.writeUTF(accessRule.mPackageName);
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 {@link #encodeRules}.
* @hide
*/
public static UiccAccessRule[] decodeRules(byte[] encodedRules) {
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.readUTF();
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.
*
* @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 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.
*
* @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 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.
*/
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;
}
}