1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.compatibility.common.deviceinfo; 17 18 import android.icu.util.ULocale; 19 import android.util.Log; 20 import androidx.annotation.Nullable; 21 import com.android.compatibility.common.util.DeviceInfoStore; 22 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.IOException; 26 import java.nio.MappedByteBuffer; 27 import java.nio.channels.FileChannel; 28 import java.security.MessageDigest; 29 import java.security.NoSuchAlgorithmException; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 import java.util.stream.Collectors; 34 35 /** 36 * Locale device info collector. 37 */ 38 public final class LocaleDeviceInfo extends DeviceInfo { 39 private static final String TAG = "LocaleDeviceInfo"; 40 41 @Override collectDeviceInfo(DeviceInfoStore store)42 protected void collectDeviceInfo(DeviceInfoStore store) throws Exception { 43 List<String> locales = Arrays.asList( 44 getInstrumentation().getContext().getAssets().getLocales()); 45 if (locales.isEmpty()) { 46 // default locale 47 locales.add("en_US"); 48 } 49 store.addListResult("locale", locales); 50 51 List<String> icuLocales = Arrays.stream(ULocale.getAvailableLocales()) 52 .map((uLocale -> uLocale.toLanguageTag())) 53 .collect(Collectors.toList()); 54 if (icuLocales.isEmpty()) { 55 // default locale 56 icuLocales.add(ULocale.US.toLanguageTag()); 57 } 58 store.addListResult("icu_locale", icuLocales); 59 60 collectLocaleDataFilesInfo(store); 61 } 62 63 /** 64 * Collect the fingerprints of ICU data files. On AOSP build, there are only 2 data files. 65 * The example paths are /apex/com.android.tzdata/etc/icu/icu_tzdata.dat and 66 * /apex/com.android.i18n/etc/icu/icudt65l.dat 67 */ collectLocaleDataFilesInfo(DeviceInfoStore store)68 private void collectLocaleDataFilesInfo(DeviceInfoStore store) throws IOException { 69 String prop = System.getProperty("android.icu.impl.ICUBinary.dataPath"); 70 store.startArray("icu_data_file_info"); 71 if (prop != null) { 72 String[] dataDirs = prop.split(":"); 73 // List all readable ".dat" files in the directories. 74 List<File> datFiles = Arrays.stream(dataDirs) 75 .filter((dir) -> dir != null && !dir.isEmpty()) 76 .map((dir) -> new File(dir)) 77 .filter((f) -> f.canRead() && f.isDirectory()) 78 .map((f) -> f.listFiles()) 79 .filter((files) -> files != null) 80 .map((files) -> Arrays.asList(files)) 81 .reduce(new ArrayList<>(), (l1, l2) -> { 82 l1.addAll(l2); 83 return l1; 84 }) 85 .stream() 86 .filter((f) -> f != null && f.canRead() && f.getName().endsWith(".dat")) 87 .collect(Collectors.toList()); 88 89 for (File datFile : datFiles) { 90 String sha256Hash = sha256(datFile); 91 92 store.startGroup(); 93 store.addResult("file_path", datFile.getPath()); 94 // Still store the null hash to indicate an error occurring when obtaining the hash. 95 store.addResult("file_sha256", sha256Hash); 96 store.endGroup(); 97 } 98 } 99 store.endArray(); 100 } 101 sha256(File file)102 public static @Nullable String sha256(File file) { 103 try (FileInputStream in = new FileInputStream(file); 104 FileChannel fileChannel = in.getChannel()) { 105 106 MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, 107 fileChannel.size()); 108 109 MessageDigest md = MessageDigest.getInstance("SHA-256"); 110 md.update(mappedByteBuffer); 111 112 byte[] digest = md.digest(); 113 StringBuilder sb = new StringBuilder(digest.length * 2); 114 for(int i = 0; i < digest.length; i++){ 115 sb.append(Character.forDigit((digest[i] >> 4) & 0xF, 16)); 116 sb.append(Character.forDigit((digest[i] & 0xF), 16)); 117 } 118 return sb.toString(); 119 } catch (IOException | NoSuchAlgorithmException e) { 120 Log.w(TAG, String.format("Can't obtain the hash of file: %s", file), e); 121 return null; 122 } 123 } 124 } 125