diff options
31 files changed, 570 insertions, 1 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 4b2a8d2..bd42e34 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -42,6 +42,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.pm.ManifestDigest; +import android.content.pm.VerifierDeviceIdentity; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -1208,6 +1209,19 @@ final class ApplicationPackageManager extends PackageManager { // TODO: } + /** + * @hide + */ + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() { + try { + return mPM.getVerifierDeviceIdentity(); + } catch (RemoteException e) { + // Should never happen! + } + return null; + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/content/pm/ActivityInfo.aidl b/core/java/android/content/pm/ActivityInfo.aidl index dd90302..dd90302 100755..100644 --- a/core/java/android/content/pm/ActivityInfo.aidl +++ b/core/java/android/content/pm/ActivityInfo.aidl diff --git a/core/java/android/content/pm/ApplicationInfo.aidl b/core/java/android/content/pm/ApplicationInfo.aidl index 006d1bd..006d1bd 100755..100644 --- a/core/java/android/content/pm/ApplicationInfo.aidl +++ b/core/java/android/content/pm/ApplicationInfo.aidl diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java index 8edd436..8edd436 100755..100644 --- a/core/java/android/content/pm/ConfigurationInfo.java +++ b/core/java/android/content/pm/ConfigurationInfo.java diff --git a/core/java/android/content/pm/FeatureInfo.aidl b/core/java/android/content/pm/FeatureInfo.aidl index d84a84c..d84a84c 100755..100644 --- a/core/java/android/content/pm/FeatureInfo.aidl +++ b/core/java/android/content/pm/FeatureInfo.aidl diff --git a/core/java/android/content/pm/IPackageDataObserver.aidl b/core/java/android/content/pm/IPackageDataObserver.aidl index d010ee4..d010ee4 100755..100644 --- a/core/java/android/content/pm/IPackageDataObserver.aidl +++ b/core/java/android/content/pm/IPackageDataObserver.aidl diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 08aef16..5e6e768 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -38,6 +38,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; +import android.content.pm.VerifierDeviceIdentity; import android.net.Uri; import android.content.IntentSender; @@ -359,4 +360,6 @@ interface IPackageManager { in ManifestDigest manifestDigest); void verifyPendingInstall(int id, boolean verified, in String message); + + VerifierDeviceIdentity getVerifierDeviceIdentity(); } diff --git a/core/java/android/content/pm/IPackageStatsObserver.aidl b/core/java/android/content/pm/IPackageStatsObserver.aidl index ede4d1d..ede4d1d 100755..100644 --- a/core/java/android/content/pm/IPackageStatsObserver.aidl +++ b/core/java/android/content/pm/IPackageStatsObserver.aidl diff --git a/core/java/android/content/pm/InstrumentationInfo.aidl b/core/java/android/content/pm/InstrumentationInfo.aidl index 3d847ae..3d847ae 100755..100644 --- a/core/java/android/content/pm/InstrumentationInfo.aidl +++ b/core/java/android/content/pm/InstrumentationInfo.aidl diff --git a/core/java/android/content/pm/ManifestDigest.aidl b/core/java/android/content/pm/ManifestDigest.aidl index ebabab0..ebabab0 100755..100644 --- a/core/java/android/content/pm/ManifestDigest.aidl +++ b/core/java/android/content/pm/ManifestDigest.aidl diff --git a/core/java/android/content/pm/PackageInfo.aidl b/core/java/android/content/pm/PackageInfo.aidl index 35e2322..35e2322 100755..100644 --- a/core/java/android/content/pm/PackageInfo.aidl +++ b/core/java/android/content/pm/PackageInfo.aidl diff --git a/core/java/android/content/pm/PackageInfoLite.aidl b/core/java/android/content/pm/PackageInfoLite.aidl index 2c80942..2c80942 100755..100644 --- a/core/java/android/content/pm/PackageInfoLite.aidl +++ b/core/java/android/content/pm/PackageInfoLite.aidl diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b4e3988..dcb6776 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2614,4 +2614,14 @@ public abstract class PackageManager { public static int getAppId(int uid) { return uid % PER_USER_RANGE; } + + /** + * Returns the device identity that verifiers can use to associate their + * scheme to a particular device. This should not be used by anything other + * than a package verifier. + * + * @return identity that uniquely identifies current device + * @hide + */ + public abstract VerifierDeviceIdentity getVerifierDeviceIdentity(); } diff --git a/core/java/android/content/pm/PackageStats.aidl b/core/java/android/content/pm/PackageStats.aidl index 8c9786f..8c9786f 100755..100644 --- a/core/java/android/content/pm/PackageStats.aidl +++ b/core/java/android/content/pm/PackageStats.aidl diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java index 1205da7..1205da7 100755..100644 --- a/core/java/android/content/pm/PackageStats.java +++ b/core/java/android/content/pm/PackageStats.java diff --git a/core/java/android/content/pm/ParceledListSlice.aidl b/core/java/android/content/pm/ParceledListSlice.aidl index c02cc6a..c02cc6a 100755..100644 --- a/core/java/android/content/pm/ParceledListSlice.aidl +++ b/core/java/android/content/pm/ParceledListSlice.aidl diff --git a/core/java/android/content/pm/PermissionGroupInfo.aidl b/core/java/android/content/pm/PermissionGroupInfo.aidl index 9f215f1..9f215f1 100755..100644 --- a/core/java/android/content/pm/PermissionGroupInfo.aidl +++ b/core/java/android/content/pm/PermissionGroupInfo.aidl diff --git a/core/java/android/content/pm/PermissionInfo.aidl b/core/java/android/content/pm/PermissionInfo.aidl index 5a7d4f4..5a7d4f4 100755..100644 --- a/core/java/android/content/pm/PermissionInfo.aidl +++ b/core/java/android/content/pm/PermissionInfo.aidl diff --git a/core/java/android/content/pm/ProviderInfo.aidl b/core/java/android/content/pm/ProviderInfo.aidl index 18fbc8a..18fbc8a 100755..100644 --- a/core/java/android/content/pm/ProviderInfo.aidl +++ b/core/java/android/content/pm/ProviderInfo.aidl diff --git a/core/java/android/content/pm/ResolveInfo.aidl b/core/java/android/content/pm/ResolveInfo.aidl index b4e7f8b..b4e7f8b 100755..100644 --- a/core/java/android/content/pm/ResolveInfo.aidl +++ b/core/java/android/content/pm/ResolveInfo.aidl diff --git a/core/java/android/content/pm/ServiceInfo.aidl b/core/java/android/content/pm/ServiceInfo.aidl index 5ddae1a..5ddae1a 100755..100644 --- a/core/java/android/content/pm/ServiceInfo.aidl +++ b/core/java/android/content/pm/ServiceInfo.aidl diff --git a/core/java/android/content/pm/Signature.aidl b/core/java/android/content/pm/Signature.aidl index 3a0d775..3a0d775 100755..100644 --- a/core/java/android/content/pm/Signature.aidl +++ b/core/java/android/content/pm/Signature.aidl diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.aidl b/core/java/android/content/pm/VerifierDeviceIdentity.aidl new file mode 100644 index 0000000..eb076ae --- /dev/null +++ b/core/java/android/content/pm/VerifierDeviceIdentity.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2011, 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.content.pm; + +parcelable VerifierDeviceIdentity; diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java new file mode 100644 index 0000000..bfebe0f --- /dev/null +++ b/core/java/android/content/pm/VerifierDeviceIdentity.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 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.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.UnsupportedEncodingException; +import java.security.SecureRandom; +import java.util.Random; + +/** + * An identity that uniquely identifies a particular device. In this + * implementation, the identity is represented as a 64-bit integer encoded to a + * 13-character string using RFC 4648's Base32 encoding without the trailing + * padding. This makes it easy for users to read and write the code without + * confusing 'I' (letter) with '1' (one) or 'O' (letter) with '0' (zero). + * + * @hide + */ +public class VerifierDeviceIdentity implements Parcelable { + /** + * Encoded size of a long (64-bit) into Base32. This format will end up + * looking like XXXX-XXXX-XXXX-X (length ignores hyphens) when applied with + * the GROUP_SIZE below. + */ + private static final int LONG_SIZE = 13; + + /** + * Size of groupings when outputting as strings. This helps people read it + * out and keep track of where they are. + */ + private static final int GROUP_SIZE = 4; + + private final long mIdentity; + + private final String mIdentityString; + + /** + * Create a verifier device identity from a long. + * + * @param identity device identity in a 64-bit integer. + * @throws + */ + public VerifierDeviceIdentity(long identity) { + mIdentity = identity; + mIdentityString = encodeBase32(identity); + } + + private VerifierDeviceIdentity(Parcel source) { + final long identity = source.readLong(); + + mIdentity = identity; + mIdentityString = encodeBase32(identity); + } + + /** + * Generate a new device identity. + * + * @return random uniformly-distributed device identity + */ + public static VerifierDeviceIdentity generate() { + final SecureRandom sr = new SecureRandom(); + return generate(sr); + } + + /** + * Generate a new device identity using a provided random number generator + * class. This is used for testing. + * + * @param rng random number generator to retrieve the next long from + * @return verifier device identity based on the input from the provided + * random number generator + */ + static VerifierDeviceIdentity generate(Random rng) { + long identity = rng.nextLong(); + return new VerifierDeviceIdentity(identity); + } + + private static final char ENCODE[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '2', '3', '4', '5', '6', '7', + }; + + private static final char SEPARATOR = '-'; + + private static final String encodeBase32(long input) { + final char[] alphabet = ENCODE; + + /* + * Make a character array with room for the separators between each + * group. + */ + final char encoded[] = new char[LONG_SIZE + (LONG_SIZE / GROUP_SIZE)]; + + int index = encoded.length; + for (int i = 0; i < LONG_SIZE; i++) { + /* + * Make sure we don't put a separator at the beginning. Since we're + * building from the rear of the array, we use (LONG_SIZE % + * GROUP_SIZE) to make the odd-size group appear at the end instead + * of the beginning. + */ + if (i > 0 && (i % GROUP_SIZE) == (LONG_SIZE % GROUP_SIZE)) { + encoded[--index] = SEPARATOR; + } + + /* + * Extract 5 bits of data, then shift it out. + */ + final int group = (int) (input & 0x1F); + input >>>= 5; + + encoded[--index] = alphabet[group]; + } + + return String.valueOf(encoded); + } + + // TODO move this out to its own class (android.util.Base32) + private static final long decodeBase32(byte[] input) throws IllegalArgumentException { + long output = 0L; + int numParsed = 0; + + final int N = input.length; + for (int i = 0; i < N; i++) { + final int group = input[i]; + + /* + * This essentially does the reverse of the ENCODED alphabet above + * without a table. A..Z are 0..25 and 2..7 are 26..31. + */ + final int value; + if ('A' <= group && group <= 'Z') { + value = group - 'A'; + } else if ('2' <= group && group <= '7') { + value = group - ('2' - 26); + } else if (group == SEPARATOR) { + continue; + } else { + throw new IllegalArgumentException("base base-32 character: " + group); + } + + output = (output << 5) | value; + numParsed++; + + if (numParsed == 1) { + if ((value & 0xF) != value) { + throw new IllegalArgumentException("illegal start character; will overflow"); + } + } else if (numParsed > 13) { + throw new IllegalArgumentException("too long; should have 13 characters"); + } + } + + if (numParsed != 13) { + throw new IllegalArgumentException("too short; should have 13 characters"); + } + + return output; + } + + @Override + public int hashCode() { + return (int) mIdentity; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof VerifierDeviceIdentity)) { + return false; + } + + final VerifierDeviceIdentity o = (VerifierDeviceIdentity) other; + return mIdentity == o.mIdentity; + } + + @Override + public String toString() { + return mIdentityString; + } + + public static VerifierDeviceIdentity parse(String deviceIdentity) + throws IllegalArgumentException { + final byte[] input; + try { + input = deviceIdentity.getBytes("US-ASCII"); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("bad base-32 characters in input"); + } + + return new VerifierDeviceIdentity(decodeBase32(input)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mIdentity); + } + + public static final Parcelable.Creator<VerifierDeviceIdentity> CREATOR + = new Parcelable.Creator<VerifierDeviceIdentity>() { + public VerifierDeviceIdentity createFromParcel(Parcel source) { + return new VerifierDeviceIdentity(source); + } + + public VerifierDeviceIdentity[] newArray(int size) { + return new VerifierDeviceIdentity[size]; + } + }; +} diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 146466f..cadc895 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -88,6 +88,7 @@ <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> <uses-permission android:name="android.permission.MOVE_PACKAGE" /> + <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" /> <!--os storage test permissions --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> diff --git a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java index 8922f27..cc8c4a6 100644 --- a/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java +++ b/core/tests/coretests/src/android/content/pm/ManifestDigestTest.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2011 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.content.pm; import android.os.Parcel; diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 68ddcc4..9575ced 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -42,6 +42,7 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.SmallTest; import android.util.DisplayMetrics; import android.util.Log; @@ -3121,6 +3122,14 @@ public class PackageManagerTests extends AndroidTestCase { invokeInstallPackageFail(Uri.fromFile(invalidFile), 0, retCode); } + @SmallTest + public void testGetVerifierDeviceIdentity() { + PackageManager pm = getPm(); + VerifierDeviceIdentity id = pm.getVerifierDeviceIdentity(); + + assertNotNull("Verifier device identity should not be null", id); + } + /*---------- Recommended install location tests ----*/ /* * TODO's diff --git a/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java new file mode 100644 index 0000000..e6a6a26 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/VerifierDeviceIdentityTest.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2011 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.content.pm; + +import android.os.Parcel; + +import java.util.Random; + +public class VerifierDeviceIdentityTest extends android.test.AndroidTestCase { + private static final long TEST_1 = 0x7A5F00FF5A55AAA5L; + + private static final String TEST_1_ENCODED = "HUXY-A75N-FLKV-F"; + + private static final long TEST_2 = 0x5A05FF5A05F0A555L; + + private static final long TEST_MAXVALUE = Long.MAX_VALUE; + + private static final String TEST_MAXVALUE_ENCODED = "H777-7777-7777-7"; + + private static final long TEST_MINVALUE = Long.MIN_VALUE; + + private static final String TEST_MINVALUE_ENCODED = "IAAA-AAAA-AAAA-A"; + + private static final long TEST_ZERO = 0L; + + private static final String TEST_ZERO_ENCODED = "AAAA-AAAA-AAAA-A"; + + private static final long TEST_NEGONE = -1L; + + private static final String TEST_NEGONE_ENCODED = "P777-7777-7777-7"; + + private static final String TEST_OVERFLOW_ENCODED = "QAAA-AAAA-AAAA-A"; + + public void testVerifierDeviceIdentity_Equals_Success() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + VerifierDeviceIdentity id2 = new VerifierDeviceIdentity(TEST_1); + + assertTrue("The two VerifierDeviceIdentity instances should be equal", id1.equals(id2)); + } + + public void testVerifierDeviceIdentity_Equals_Failure() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + VerifierDeviceIdentity id2 = new VerifierDeviceIdentity(TEST_2); + + assertFalse("The two VerifierDeviceIdentity instances should be unique", id1.equals(id2)); + } + + public void testVerifierDeviceIdentity_HashCode() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + + assertEquals("The VerifierDeviceIdentity should have the same hashcode as its identity", + (int) TEST_1, id1.hashCode()); + } + + public void testVerifierDeviceIdentity_ToString_Success() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + + assertEquals("The identity should encode correctly to the expected Base 32 string", + TEST_1_ENCODED, id1.toString()); + } + + public void testVerifierDeviceIdentity_ToString_Largest() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MAXVALUE); + + assertEquals("The identity should encode correctly to the expected Base 32 string", + TEST_MAXVALUE_ENCODED, id1.toString()); + } + + public void testVerifierDeviceIdentity_ToString_Zero() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_ZERO); + + assertEquals("The identity should encode correctly to the expected Base 32 string", + TEST_ZERO_ENCODED, id1.toString()); + } + + public void testVerifierDeviceIdentity_ToString_NegOne() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_NEGONE); + + assertEquals("The identity should encode correctly to the expected Base 32 string", + TEST_NEGONE_ENCODED, id1.toString()); + } + + public void testVerifierDeviceIdentity_ToString_MinValue() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MINVALUE); + + assertEquals("The identity should encode correctly to the expected Base 32 string", + TEST_MINVALUE_ENCODED, id1.toString()); + } + + public void testVerifierDeviceIdentity_Parcel_ReadNegative() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MINVALUE); + + Parcel parcel = Parcel.obtain(); + parcel.writeLong(TEST_MINVALUE); + parcel.setDataPosition(0); + + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.CREATOR.createFromParcel(parcel); + + assertEquals("Parcel created should match expected value", id1, id2); + } + + public void testVerifierDeviceIdentity_Parcel_Read_Pass() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + + Parcel parcel = Parcel.obtain(); + id1.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.CREATOR.createFromParcel(parcel); + + assertEquals("Original identity and parceled identity should be the same", id1, id2); + } + + private static class MockRandom extends Random { + private long mNextLong; + + public MockRandom() { + } + + public void setNextLong(long nextLong) { + mNextLong = nextLong; + } + + @Override + public long nextLong() { + return mNextLong; + } + } + + public void testVerifierDeviceIdentity_Generate_MinValue() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MINVALUE); + + MockRandom random = new MockRandom(); + random.setNextLong(Long.MIN_VALUE); + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.generate(random); + + assertEquals("Identity created from Long.MIN_VALUE and one created from return from RNG" + + " should be the same", id1, id2); + } + + public void testVerifierDeviceIdentity_Generate_Random() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + + MockRandom random = new MockRandom(); + random.setNextLong(TEST_1); + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.generate(random); + + assertEquals("Identity should end up being same when coming from RNG", id1, id2); + } + + public void testVerifierDeviceIdentity_Parse_Normal() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_1); + + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.parse(TEST_1_ENCODED); + + assertEquals("Parsed device identity should have the same value as original identity", + id1, id2); + } + + public void testVerifierDeviceIdentity_Parse_MaxValue() { + VerifierDeviceIdentity id1 = new VerifierDeviceIdentity(TEST_MAXVALUE); + + VerifierDeviceIdentity id2 = VerifierDeviceIdentity.parse(TEST_MAXVALUE_ENCODED); + + assertEquals("Original max value and parsed max value should be equal", id1, id2); + } + + public void testVerifierDeviceIdentity_Parse_TooShort() { + try { + VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-"); + fail("Parsing should fail when device identifier is too short"); + } catch (IllegalArgumentException e) { + // success + } + } + + public void testVerifierDeviceIdentity_Parse_WayTooShort() { + try { + VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("----------------"); + fail("Parsing should fail when device identifier is too short"); + } catch (IllegalArgumentException e) { + // success + } + } + + public void testVerifierDeviceIdentity_Parse_TooLong() { + try { + VerifierDeviceIdentity id = VerifierDeviceIdentity.parse("AAAA-AAAA-AAAA-AA"); + fail("Parsing should fail when device identifier is too long"); + } catch (IllegalArgumentException e) { + // success + } + } + + public void testVerifierDeviceIdentity_Parse_Overflow() { + try { + VerifierDeviceIdentity id = VerifierDeviceIdentity.parse(TEST_OVERFLOW_ENCODED); + fail("Parsing should fail when the value will overflow"); + } catch (IllegalArgumentException e) { + // success + } + } +} diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 4e5ca8e..9ebdd52 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -71,6 +71,7 @@ import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.pm.ManifestDigest; +import android.content.pm.VerifierDeviceIdentity; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -8405,4 +8406,15 @@ public class PackageManagerService extends IPackageManager.Stub { mUserManager.removeUser(userId); return true; } + + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + "Only package verification agents can read the verifier device identity"); + + synchronized (mPackages) { + return mSettings.getVerifierDeviceIdentityLPw(); + } + } } diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index f270003..7cdb5b1 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -39,6 +39,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.content.pm.Signature; +import android.content.pm.VerifierDeviceIdentity; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; @@ -86,7 +87,10 @@ final class Settings { // used to grant newer permissions one time during a system upgrade. int mInternalSdkPlatform; int mExternalSdkPlatform; - + + /** Device identity for the purpose of package verification. */ + private VerifierDeviceIdentity mVerifierDeviceIdentity; + // The user's preferred activities associated with particular intent // filters. final IntentResolver<PreferredActivity, PreferredActivity> mPreferredActivities = @@ -865,6 +869,12 @@ final class Settings { serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform)); serializer.endTag(null, "last-platform-version"); + if (mVerifierDeviceIdentity != null) { + serializer.startTag(null, "verifier"); + serializer.attribute(null, "device", mVerifierDeviceIdentity.toString()); + serializer.endTag(null, "verifier"); + } + serializer.startTag(null, "permission-trees"); for (BasePermission bp : mPermissionTrees.values()) { writePermissionLPr(serializer, bp); @@ -1280,6 +1290,14 @@ final class Settings { } } catch (NumberFormatException e) { } + } else if (tagName.equals("verifier")) { + final String deviceIdentity = parser.getAttributeValue(null, "device"); + try { + mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity); + } catch (IllegalArgumentException e) { + Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: " + + e.getMessage()); + } } else { Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: " + parser.getName()); @@ -1894,6 +1912,16 @@ final class Settings { return PackageManagerService.FIRST_APPLICATION_UID + N; } + public VerifierDeviceIdentity getVerifierDeviceIdentityLPw() { + if (mVerifierDeviceIdentity == null) { + mVerifierDeviceIdentity = VerifierDeviceIdentity.generate(); + + writeLPr(); + } + + return mVerifierDeviceIdentity; + } + public PackageSetting getDisabledSystemPkgLPr(String name) { PackageSetting ps = mDisabledSysPackages.get(name); return ps; diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 501c219..f2fb36f 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -38,6 +38,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.content.pm.ManifestDigest; +import android.content.pm.VerifierDeviceIdentity; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -552,4 +553,12 @@ public class MockPackageManager extends PackageManager { public void verifyPendingInstall(int id, boolean verified, String failureMessage) { throw new UnsupportedOperationException(); } + + /** + * @hide + */ + @Override + public VerifierDeviceIdentity getVerifierDeviceIdentity() { + throw new UnsupportedOperationException(); + } } |