Loading core/java/android/util/ByteStringUtils.java 0 → 100644 +82 −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 android.util; /** * A utility class for common byte array to hex string operations and vise versa. * * @hide */ public final class ByteStringUtils { private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private ByteStringUtils() { /* hide constructor */ } /** * Returns the hex encoded string representation of bytes. * @param bytes Byte array to encode. * @return Hex encoded string representation of bytes. */ public static String toString(byte[] bytes) { if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { return null; } final int byteLength = bytes.length; final int charCount = 2 * byteLength; final char[] chars = new char[charCount]; for (int i = 0; i < byteLength; i++) { final int byteHex = bytes[i] & 0xFF; chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; } return new String(chars); } /** * Returns the decoded byte array representation of str. * @param str Hex encoded string to decode. * @return Decoded byte array representation of str. */ public static byte[] toByteArray(String str) { if (str == null || str.length() == 0 || str.length() % 2 != 0) { return null; } final char[] chars = str.toCharArray(); final int charLength = chars.length; final byte[] bytes = new byte[charLength / 2]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F)); } return bytes; } private static int getIndex(char c) { for (int i = 0; i < HEX_ARRAY.length; i++) { if (HEX_ARRAY[i] == c) { return i; } } return -1; } } core/java/android/util/PackageUtils.java +1 −12 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import java.security.NoSuchAlgorithmException; * @hide */ public final class PackageUtils { private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private PackageUtils() { /* hide constructor */ Loading Loading @@ -81,16 +80,6 @@ public final class PackageUtils { messageDigest.update(data); final byte[] digest = messageDigest.digest(); final int digestLength = digest.length; final int charCount = 2 * digestLength; final char[] chars = new char[charCount]; for (int i = 0; i < digestLength; i++) { final int byteHex = digest[i] & 0xFF; chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; } return new String(chars); return ByteStringUtils.toString(messageDigest.digest()); } } core/tests/coretests/src/android/provider/SettingsProviderTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -364,4 +364,20 @@ public class SettingsProviderTest extends AndroidTestCase { // one or more activity can handle this intent. assertTrue(resolveInfoList.size() > 0); } @SmallTest public void testValidSsaid() { ContentResolver r = getContext().getContentResolver(); // Verify ssaid String ssaid = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID); assertTrue(ssaid != null); assertTrue(ssaid.length() == 16); String ssaid2 = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID); assertTrue(ssaid2 != null); assertTrue(ssaid2.length() == 16); assertTrue(ssaid.equals(ssaid2)); } } packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +256 −5 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.provider.Settings.Global; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ByteStringUtils; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading @@ -76,9 +77,13 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; Loading Loading @@ -162,6 +167,7 @@ public class SettingsProvider extends ContentProvider { public static final int SETTINGS_TYPE_GLOBAL = 0; public static final int SETTINGS_TYPE_SYSTEM = 1; public static final int SETTINGS_TYPE_SECURE = 2; public static final int SETTINGS_TYPE_SSAID = 3; public static final int SETTINGS_TYPE_MASK = 0xF0000000; public static final int SETTINGS_TYPE_SHIFT = 28; Loading Loading @@ -249,6 +255,9 @@ public class SettingsProvider extends ContentProvider { case SETTINGS_TYPE_SYSTEM: { return "SETTINGS_SYSTEM"; } case SETTINGS_TYPE_SSAID: { return "SETTINGS_SSAID"; } default: { return "UNKNOWN"; } Loading Loading @@ -704,6 +713,13 @@ public class SettingsProvider extends ContentProvider { UserHandle.getUserId(uid)); } } @Override public void onUidRemoved(int uid) { synchronized (mLock) { mSettingsRegistry.onUidRemovedLocked(uid); } } }; // package changes Loading Loading @@ -957,8 +973,15 @@ public class SettingsProvider extends ContentProvider { continue; } Setting setting = mSettingsRegistry.getSettingLocked( SETTINGS_TYPE_SECURE, owningUserId, name); // As of Android O (API 24), the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. final Setting setting; if (isNewSsaidSetting(name)) { setting = getSsaidSettingLocked(owningUserId); } else { setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); } appendSettingToCursor(result, setting); } Loading Loading @@ -986,11 +1009,43 @@ public class SettingsProvider extends ContentProvider { // Get the value. synchronized (mLock) { // As of Android O (API 24), the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. if (isNewSsaidSetting(name)) { return getSsaidSettingLocked(owningUserId); } return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); } } private boolean isNewSsaidSetting(String name) { return Settings.Secure.ANDROID_ID.equals(name) && UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID; } private Setting getSsaidSettingLocked(int owningUserId) { // Get uid of caller (key) used to store ssaid value String name = Integer.toString( UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid()))); if (DEBUG) { Slog.v(LOG_TAG, "getSsaidSettingLocked(" + name + "," + owningUserId + ")"); } // Retrieve the ssaid from the table if present. final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name); // Lazy initialize ssaid if not yet present in ssaid table. if (ssaid.isNull() || ssaid.getValue() == null) { return mSettingsRegistry.generateSsaidLocked(getCallingPackage(), owningUserId); } return ssaid; } private boolean insertSecureSetting(String name, String value, String tag, boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { Loading Loading @@ -1818,6 +1873,9 @@ public class SettingsProvider extends ContentProvider { private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml"; private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml"; private static final String SETTINGS_FILE_SECURE = "settings_secure.xml"; private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml"; private static final String SSAID_USER_KEY = "userkey"; private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>(); Loading @@ -1832,6 +1890,115 @@ public class SettingsProvider extends ContentProvider { mGenerationRegistry = new GenerationRegistry(mLock); mBackupManager = new BackupManager(getContext()); migrateAllLegacySettingsIfNeeded(); syncSsaidTableOnStart(); } private void generateUserKeyLocked(int userId) { // Generate a random key for each user used for creating a new ssaid. final byte[] keyBytes = new byte[16]; final SecureRandom rand = new SecureRandom(); rand.nextBytes(keyBytes); // Convert to string for storage in settings table. final String userKey = ByteStringUtils.toString(keyBytes); // Store the key in the ssaid table. final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); final boolean success = ssaidSettings.insertSettingLocked(SSAID_USER_KEY, userKey, null, true, SettingsState.SYSTEM_PACKAGE_NAME); if (!success) { throw new IllegalStateException("Ssaid settings not accessible"); } } public Setting generateSsaidLocked(String packageName, int userId) { final PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES, userId); } catch (RemoteException e) { throw new IllegalStateException("Package info doesn't exist"); } // Read the user's key from the ssaid table. Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); if (userKeySetting.isNull() || userKeySetting.getValue() == null) { // Lazy initialize and store the user key. generateUserKeyLocked(userId); userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); if (userKeySetting.isNull() || userKeySetting.getValue() == null) { throw new IllegalStateException("User key not accessible"); } } final String userKey = userKeySetting.getValue(); // Convert the user's key back to a byte array. final byte[] keyBytes = ByteStringUtils.toByteArray(userKey); if (keyBytes == null || keyBytes.length != 16) { throw new IllegalStateException("User key invalid"); } final MessageDigest md; try { // Hash package name and signature. md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("HmacSHA256 is not available"); } md.update(keyBytes); md.update(packageInfo.packageName.getBytes(StandardCharsets.UTF_8)); md.update(packageInfo.signatures[0].toByteArray()); // Convert result to a string for storage in settings table. Only want first 64 bits. final String ssaid = ByteStringUtils.toString(md.digest()).substring(0, 16) .toLowerCase(); // Save the ssaid in the ssaid table. final String uid = Integer.toString(packageInfo.applicationInfo.uid); final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true, packageName); if (!success) { throw new IllegalStateException("Ssaid settings not accessible"); } return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid); } public void syncSsaidTableOnStart() { synchronized (mLock) { // Verify that each user's packages and ssaid's are in sync. for (UserInfo user : mUserManager.getUsers(true)) { // Get all uids for the user's packages. final List<PackageInfo> packages; try { packages = mPackageManager.getInstalledPackages(0, user.id).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } final Set<String> appUids = new HashSet<>(); for (PackageInfo info : packages) { appUids.add(Integer.toString(info.applicationInfo.uid)); } // Get all uids currently stored in the user's ssaid table. final Set<String> ssaidUids = new HashSet<>( getSettingsNamesLocked(SETTINGS_TYPE_SSAID, user.id)); ssaidUids.remove(SSAID_USER_KEY); // Perform a set difference for the appUids and ssaidUids. ssaidUids.removeAll(appUids); // If there are ssaidUids left over they need to be removed from the table. final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, user.id); for (String uid : ssaidUids) { ssaidSettings.deleteSettingLocked(uid); } } } } public List<String> getSettingsNamesLocked(int type, int userId) { Loading Loading @@ -1884,6 +2051,10 @@ public class SettingsProvider extends ContentProvider { final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); ensureSettingsStateLocked(systemKey); // Ensure secure settings loaded. final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId); ensureSettingsStateLocked(ssaidKey); // Upgrade the settings to the latest version. UpgradeController upgrader = new UpgradeController(userId); upgrader.upgradeIfNeededLocked(); Loading Loading @@ -1936,6 +2107,23 @@ public class SettingsProvider extends ContentProvider { } } // Nuke ssaid settings. final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId); final SettingsState ssaidSettingsState = mSettingsStates.get(ssaidKey); if (ssaidSettingsState != null) { if (permanently) { mSettingsStates.remove(ssaidKey); ssaidSettingsState.destroyLocked(null); } else { ssaidSettingsState.destroyLocked(new Runnable() { @Override public void run() { mSettingsStates.remove(ssaidKey); } }); } } // Nuke generation tracking data mGenerationRegistry.onUserRemoved(userId); } Loading Loading @@ -1977,8 +2165,10 @@ public class SettingsProvider extends ContentProvider { SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState == null) { return null; return settingsState.getNullSetting(); } // getSettingLocked will return non-null result return settingsState.getSettingLocked(name); } Loading Loading @@ -2079,6 +2269,12 @@ public class SettingsProvider extends ContentProvider { } } public void onUidRemovedLocked(int uid) { final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, UserHandle.getUserId(uid)); ssaidSettings.deleteSettingLocked(Integer.toString(uid)); } private SettingsState peekSettingsStateLocked(int key) { SettingsState settingsState = mSettingsStates.get(key); if (settingsState != null) { Loading Loading @@ -2300,6 +2496,10 @@ public class SettingsProvider extends ContentProvider { return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; } private boolean isSsaidSettingsKey(int key) { return getTypeFromKey(key) == SETTINGS_TYPE_SSAID; } private File getSettingsFile(int key) { if (isGlobalSettingsKey(key)) { final int userId = getUserIdFromKey(key); Loading @@ -2313,6 +2513,10 @@ public class SettingsProvider extends ContentProvider { final int userId = getUserIdFromKey(key); return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILE_SECURE); } else if (isSsaidSettingsKey(key)) { final int userId = getUserIdFromKey(key); return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILE_SSAID); } else { throw new IllegalArgumentException("Invalid settings key:" + key); } Loading @@ -2336,7 +2540,8 @@ public class SettingsProvider extends ContentProvider { private int getMaxBytesPerPackageForType(int type) { switch (type) { case SETTINGS_TYPE_GLOBAL: case SETTINGS_TYPE_SECURE: { case SETTINGS_TYPE_SECURE: case SETTINGS_TYPE_SSAID: { return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED; } Loading Loading @@ -2374,7 +2579,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { private static final int SETTINGS_VERSION = 136; private static final int SETTINGS_VERSION = 137; private final int mUserId; Loading Loading @@ -2447,6 +2652,10 @@ public class SettingsProvider extends ContentProvider { return getSettingsLocked(SETTINGS_TYPE_SECURE, userId); } private SettingsState getSsaidSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SSAID, userId); } private SettingsState getSystemSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId); } Loading Loading @@ -2776,6 +2985,48 @@ public class SettingsProvider extends ContentProvider { currentVersion = 136; } if (currentVersion == 136) { // Version 136: Store legacy SSAID for all apps currently installed on the // device as first step in migrating SSAID to be unique per application. final boolean isUpgrade; try { isUpgrade = mPackageManager.isUpgrade(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } // Only retain legacy ssaid if the device is performing an OTA. After wiping // user data or first boot on a new device should use new ssaid generation. if (isUpgrade) { // Retrieve the legacy ssaid from the secure settings table. final String legacySsaid = getSettingLocked(SETTINGS_TYPE_SECURE, userId, Settings.Secure.ANDROID_ID).getValue(); // Fill each uid with the legacy ssaid to be backwards compatible. final List<PackageInfo> packages; try { packages = mPackageManager.getInstalledPackages(0, userId).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } final SettingsState ssaidSettings = getSsaidSettingsLocked(userId); for (PackageInfo info : packages) { // Check if the UID already has an entry in the table. final String uid = Integer.toString(info.applicationInfo.uid); final Setting ssaid = ssaidSettings.getSettingLocked(uid); if (ssaid.isNull() || ssaid.getValue() == null) { // Android Id doesn't exist for this package so create it. ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true, info.packageName); } } } currentVersion = 137; } if (currentVersion != newVersion) { Slog.wtf("SettingsProvider", "warning: upgrading settings database to version " + newVersion + " left it at " Loading Loading
core/java/android/util/ByteStringUtils.java 0 → 100644 +82 −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 android.util; /** * A utility class for common byte array to hex string operations and vise versa. * * @hide */ public final class ByteStringUtils { private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private ByteStringUtils() { /* hide constructor */ } /** * Returns the hex encoded string representation of bytes. * @param bytes Byte array to encode. * @return Hex encoded string representation of bytes. */ public static String toString(byte[] bytes) { if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { return null; } final int byteLength = bytes.length; final int charCount = 2 * byteLength; final char[] chars = new char[charCount]; for (int i = 0; i < byteLength; i++) { final int byteHex = bytes[i] & 0xFF; chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; } return new String(chars); } /** * Returns the decoded byte array representation of str. * @param str Hex encoded string to decode. * @return Decoded byte array representation of str. */ public static byte[] toByteArray(String str) { if (str == null || str.length() == 0 || str.length() % 2 != 0) { return null; } final char[] chars = str.toCharArray(); final int charLength = chars.length; final byte[] bytes = new byte[charLength / 2]; for (int i = 0; i < bytes.length; i++) { bytes[i] = (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F)); } return bytes; } private static int getIndex(char c) { for (int i = 0; i < HEX_ARRAY.length; i++) { if (HEX_ARRAY[i] == c) { return i; } } return -1; } }
core/java/android/util/PackageUtils.java +1 −12 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import java.security.NoSuchAlgorithmException; * @hide */ public final class PackageUtils { private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); private PackageUtils() { /* hide constructor */ Loading Loading @@ -81,16 +80,6 @@ public final class PackageUtils { messageDigest.update(data); final byte[] digest = messageDigest.digest(); final int digestLength = digest.length; final int charCount = 2 * digestLength; final char[] chars = new char[charCount]; for (int i = 0; i < digestLength; i++) { final int byteHex = digest[i] & 0xFF; chars[i * 2] = HEX_ARRAY[byteHex >>> 4]; chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F]; } return new String(chars); return ByteStringUtils.toString(messageDigest.digest()); } }
core/tests/coretests/src/android/provider/SettingsProviderTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -364,4 +364,20 @@ public class SettingsProviderTest extends AndroidTestCase { // one or more activity can handle this intent. assertTrue(resolveInfoList.size() > 0); } @SmallTest public void testValidSsaid() { ContentResolver r = getContext().getContentResolver(); // Verify ssaid String ssaid = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID); assertTrue(ssaid != null); assertTrue(ssaid.length() == 16); String ssaid2 = Settings.Secure.getString(r, Settings.Secure.ANDROID_ID); assertTrue(ssaid2 != null); assertTrue(ssaid2.length() == 16); assertTrue(ssaid.equals(ssaid2)); } }
packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +256 −5 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import android.provider.Settings.Global; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.ByteStringUtils; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading @@ -76,9 +77,13 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; Loading Loading @@ -162,6 +167,7 @@ public class SettingsProvider extends ContentProvider { public static final int SETTINGS_TYPE_GLOBAL = 0; public static final int SETTINGS_TYPE_SYSTEM = 1; public static final int SETTINGS_TYPE_SECURE = 2; public static final int SETTINGS_TYPE_SSAID = 3; public static final int SETTINGS_TYPE_MASK = 0xF0000000; public static final int SETTINGS_TYPE_SHIFT = 28; Loading Loading @@ -249,6 +255,9 @@ public class SettingsProvider extends ContentProvider { case SETTINGS_TYPE_SYSTEM: { return "SETTINGS_SYSTEM"; } case SETTINGS_TYPE_SSAID: { return "SETTINGS_SSAID"; } default: { return "UNKNOWN"; } Loading Loading @@ -704,6 +713,13 @@ public class SettingsProvider extends ContentProvider { UserHandle.getUserId(uid)); } } @Override public void onUidRemoved(int uid) { synchronized (mLock) { mSettingsRegistry.onUidRemovedLocked(uid); } } }; // package changes Loading Loading @@ -957,8 +973,15 @@ public class SettingsProvider extends ContentProvider { continue; } Setting setting = mSettingsRegistry.getSettingLocked( SETTINGS_TYPE_SECURE, owningUserId, name); // As of Android O (API 24), the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. final Setting setting; if (isNewSsaidSetting(name)) { setting = getSsaidSettingLocked(owningUserId); } else { setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); } appendSettingToCursor(result, setting); } Loading Loading @@ -986,11 +1009,43 @@ public class SettingsProvider extends ContentProvider { // Get the value. synchronized (mLock) { // As of Android O (API 24), the SSAID is read from an app-specific entry in table // SETTINGS_FILE_SSAID, unless accessed by a system process. if (isNewSsaidSetting(name)) { return getSsaidSettingLocked(owningUserId); } return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SECURE, owningUserId, name); } } private boolean isNewSsaidSetting(String name) { return Settings.Secure.ANDROID_ID.equals(name) && UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID; } private Setting getSsaidSettingLocked(int owningUserId) { // Get uid of caller (key) used to store ssaid value String name = Integer.toString( UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid()))); if (DEBUG) { Slog.v(LOG_TAG, "getSsaidSettingLocked(" + name + "," + owningUserId + ")"); } // Retrieve the ssaid from the table if present. final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name); // Lazy initialize ssaid if not yet present in ssaid table. if (ssaid.isNull() || ssaid.getValue() == null) { return mSettingsRegistry.generateSsaidLocked(getCallingPackage(), owningUserId); } return ssaid; } private boolean insertSecureSetting(String name, String value, String tag, boolean makeDefault, int requestingUserId, boolean forceNotify) { if (DEBUG) { Loading Loading @@ -1818,6 +1873,9 @@ public class SettingsProvider extends ContentProvider { private static final String SETTINGS_FILE_GLOBAL = "settings_global.xml"; private static final String SETTINGS_FILE_SYSTEM = "settings_system.xml"; private static final String SETTINGS_FILE_SECURE = "settings_secure.xml"; private static final String SETTINGS_FILE_SSAID = "settings_ssaid.xml"; private static final String SSAID_USER_KEY = "userkey"; private final SparseArray<SettingsState> mSettingsStates = new SparseArray<>(); Loading @@ -1832,6 +1890,115 @@ public class SettingsProvider extends ContentProvider { mGenerationRegistry = new GenerationRegistry(mLock); mBackupManager = new BackupManager(getContext()); migrateAllLegacySettingsIfNeeded(); syncSsaidTableOnStart(); } private void generateUserKeyLocked(int userId) { // Generate a random key for each user used for creating a new ssaid. final byte[] keyBytes = new byte[16]; final SecureRandom rand = new SecureRandom(); rand.nextBytes(keyBytes); // Convert to string for storage in settings table. final String userKey = ByteStringUtils.toString(keyBytes); // Store the key in the ssaid table. final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); final boolean success = ssaidSettings.insertSettingLocked(SSAID_USER_KEY, userKey, null, true, SettingsState.SYSTEM_PACKAGE_NAME); if (!success) { throw new IllegalStateException("Ssaid settings not accessible"); } } public Setting generateSsaidLocked(String packageName, int userId) { final PackageInfo packageInfo; try { packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES, userId); } catch (RemoteException e) { throw new IllegalStateException("Package info doesn't exist"); } // Read the user's key from the ssaid table. Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); if (userKeySetting.isNull() || userKeySetting.getValue() == null) { // Lazy initialize and store the user key. generateUserKeyLocked(userId); userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY); if (userKeySetting.isNull() || userKeySetting.getValue() == null) { throw new IllegalStateException("User key not accessible"); } } final String userKey = userKeySetting.getValue(); // Convert the user's key back to a byte array. final byte[] keyBytes = ByteStringUtils.toByteArray(userKey); if (keyBytes == null || keyBytes.length != 16) { throw new IllegalStateException("User key invalid"); } final MessageDigest md; try { // Hash package name and signature. md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("HmacSHA256 is not available"); } md.update(keyBytes); md.update(packageInfo.packageName.getBytes(StandardCharsets.UTF_8)); md.update(packageInfo.signatures[0].toByteArray()); // Convert result to a string for storage in settings table. Only want first 64 bits. final String ssaid = ByteStringUtils.toString(md.digest()).substring(0, 16) .toLowerCase(); // Save the ssaid in the ssaid table. final String uid = Integer.toString(packageInfo.applicationInfo.uid); final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId); final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true, packageName); if (!success) { throw new IllegalStateException("Ssaid settings not accessible"); } return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid); } public void syncSsaidTableOnStart() { synchronized (mLock) { // Verify that each user's packages and ssaid's are in sync. for (UserInfo user : mUserManager.getUsers(true)) { // Get all uids for the user's packages. final List<PackageInfo> packages; try { packages = mPackageManager.getInstalledPackages(0, user.id).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } final Set<String> appUids = new HashSet<>(); for (PackageInfo info : packages) { appUids.add(Integer.toString(info.applicationInfo.uid)); } // Get all uids currently stored in the user's ssaid table. final Set<String> ssaidUids = new HashSet<>( getSettingsNamesLocked(SETTINGS_TYPE_SSAID, user.id)); ssaidUids.remove(SSAID_USER_KEY); // Perform a set difference for the appUids and ssaidUids. ssaidUids.removeAll(appUids); // If there are ssaidUids left over they need to be removed from the table. final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, user.id); for (String uid : ssaidUids) { ssaidSettings.deleteSettingLocked(uid); } } } } public List<String> getSettingsNamesLocked(int type, int userId) { Loading Loading @@ -1884,6 +2051,10 @@ public class SettingsProvider extends ContentProvider { final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId); ensureSettingsStateLocked(systemKey); // Ensure secure settings loaded. final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId); ensureSettingsStateLocked(ssaidKey); // Upgrade the settings to the latest version. UpgradeController upgrader = new UpgradeController(userId); upgrader.upgradeIfNeededLocked(); Loading Loading @@ -1936,6 +2107,23 @@ public class SettingsProvider extends ContentProvider { } } // Nuke ssaid settings. final int ssaidKey = makeKey(SETTINGS_TYPE_SSAID, userId); final SettingsState ssaidSettingsState = mSettingsStates.get(ssaidKey); if (ssaidSettingsState != null) { if (permanently) { mSettingsStates.remove(ssaidKey); ssaidSettingsState.destroyLocked(null); } else { ssaidSettingsState.destroyLocked(new Runnable() { @Override public void run() { mSettingsStates.remove(ssaidKey); } }); } } // Nuke generation tracking data mGenerationRegistry.onUserRemoved(userId); } Loading Loading @@ -1977,8 +2165,10 @@ public class SettingsProvider extends ContentProvider { SettingsState settingsState = peekSettingsStateLocked(key); if (settingsState == null) { return null; return settingsState.getNullSetting(); } // getSettingLocked will return non-null result return settingsState.getSettingLocked(name); } Loading Loading @@ -2079,6 +2269,12 @@ public class SettingsProvider extends ContentProvider { } } public void onUidRemovedLocked(int uid) { final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, UserHandle.getUserId(uid)); ssaidSettings.deleteSettingLocked(Integer.toString(uid)); } private SettingsState peekSettingsStateLocked(int key) { SettingsState settingsState = mSettingsStates.get(key); if (settingsState != null) { Loading Loading @@ -2300,6 +2496,10 @@ public class SettingsProvider extends ContentProvider { return getTypeFromKey(key) == SETTINGS_TYPE_SECURE; } private boolean isSsaidSettingsKey(int key) { return getTypeFromKey(key) == SETTINGS_TYPE_SSAID; } private File getSettingsFile(int key) { if (isGlobalSettingsKey(key)) { final int userId = getUserIdFromKey(key); Loading @@ -2313,6 +2513,10 @@ public class SettingsProvider extends ContentProvider { final int userId = getUserIdFromKey(key); return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILE_SECURE); } else if (isSsaidSettingsKey(key)) { final int userId = getUserIdFromKey(key); return new File(Environment.getUserSystemDirectory(userId), SETTINGS_FILE_SSAID); } else { throw new IllegalArgumentException("Invalid settings key:" + key); } Loading @@ -2336,7 +2540,8 @@ public class SettingsProvider extends ContentProvider { private int getMaxBytesPerPackageForType(int type) { switch (type) { case SETTINGS_TYPE_GLOBAL: case SETTINGS_TYPE_SECURE: { case SETTINGS_TYPE_SECURE: case SETTINGS_TYPE_SSAID: { return SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED; } Loading Loading @@ -2374,7 +2579,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { private static final int SETTINGS_VERSION = 136; private static final int SETTINGS_VERSION = 137; private final int mUserId; Loading Loading @@ -2447,6 +2652,10 @@ public class SettingsProvider extends ContentProvider { return getSettingsLocked(SETTINGS_TYPE_SECURE, userId); } private SettingsState getSsaidSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SSAID, userId); } private SettingsState getSystemSettingsLocked(int userId) { return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId); } Loading Loading @@ -2776,6 +2985,48 @@ public class SettingsProvider extends ContentProvider { currentVersion = 136; } if (currentVersion == 136) { // Version 136: Store legacy SSAID for all apps currently installed on the // device as first step in migrating SSAID to be unique per application. final boolean isUpgrade; try { isUpgrade = mPackageManager.isUpgrade(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } // Only retain legacy ssaid if the device is performing an OTA. After wiping // user data or first boot on a new device should use new ssaid generation. if (isUpgrade) { // Retrieve the legacy ssaid from the secure settings table. final String legacySsaid = getSettingLocked(SETTINGS_TYPE_SECURE, userId, Settings.Secure.ANDROID_ID).getValue(); // Fill each uid with the legacy ssaid to be backwards compatible. final List<PackageInfo> packages; try { packages = mPackageManager.getInstalledPackages(0, userId).getList(); } catch (RemoteException e) { throw new IllegalStateException("Package manager not available"); } final SettingsState ssaidSettings = getSsaidSettingsLocked(userId); for (PackageInfo info : packages) { // Check if the UID already has an entry in the table. final String uid = Integer.toString(info.applicationInfo.uid); final Setting ssaid = ssaidSettings.getSettingLocked(uid); if (ssaid.isNull() || ssaid.getValue() == null) { // Android Id doesn't exist for this package so create it. ssaidSettings.insertSettingLocked(uid, legacySsaid, null, true, info.packageName); } } } currentVersion = 137; } if (currentVersion != newVersion) { Slog.wtf("SettingsProvider", "warning: upgrading settings database to version " + newVersion + " left it at " Loading