1 /* 2 * Copyright (C) 2022 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.geotz.app; 17 18 import android.content.Context; 19 20 import androidx.annotation.NonNull; 21 22 import com.android.timezone.location.provider.GeoDataFileManager; 23 import com.google.common.hash.Hashing; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.nio.file.Files; 29 import java.nio.file.StandardCopyOption; 30 import java.util.Objects; 31 import java.util.Properties; 32 33 /** 34 * An implementation of {@link GeoDataFileManager} that copies the tzs2.dat classpath resource to 35 * an app file path for reading. 36 */ 37 public final class GeoDataFileManagerImpl implements GeoDataFileManager { 38 39 /** 40 * The config properties key to get the resource path of the tzs2.dat file to use for time zone 41 * boundaries. 42 */ 43 private static final String RESOURCE_CONFIG_KEY_GEODATA_RESOURCE = "geodata.resource"; 44 45 /** 46 * The config properties key to get the location of the tzs2.dat file to use for time zone 47 * boundaries. 48 */ 49 private static final String RESOURCE_CONFIG_KEY_GEODATA_SHA256= "geodata.sha256"; 50 51 private File mGeoDataFile; 52 53 @Override init(@onNull Context context, @NonNull Properties configProperties)54 public void init(@NonNull Context context, @NonNull Properties configProperties) 55 throws IOException { 56 File geoDataFile = new File(context.getFilesDir(), "tzs2.dat"); 57 ClassLoader classLoader = getClass().getClassLoader(); 58 59 String geoDataResource = configProperties.getProperty(RESOURCE_CONFIG_KEY_GEODATA_RESOURCE); 60 String expectedSha256 = configProperties.getProperty(RESOURCE_CONFIG_KEY_GEODATA_SHA256); 61 if (!geoDataFile.exists() || !Objects.equals(computeSha256(geoDataFile), expectedSha256)) { 62 // Copy the resource to the app persistent files dir. 63 copyResource(classLoader, geoDataResource, geoDataFile, expectedSha256); 64 } 65 66 mGeoDataFile = geoDataFile; 67 } 68 69 @Override 70 @NonNull getGeoDataFile()71 public File getGeoDataFile() { 72 return Objects.requireNonNull(mGeoDataFile); 73 } 74 copyResource(@onNull ClassLoader classLoader, @NonNull String resourceName, @NonNull File targetPath, @NonNull String expectedSha256)75 private static void copyResource(@NonNull ClassLoader classLoader, @NonNull String resourceName, 76 @NonNull File targetPath, @NonNull String expectedSha256) { 77 boolean success = false; 78 try (InputStream resourceStream = classLoader.getResourceAsStream(resourceName)) { 79 if (resourceStream == null) { 80 throw new IllegalStateException("Unable to find " 81 + " resource=" + resourceName); 82 } 83 Files.copy(resourceStream, targetPath.toPath(), StandardCopyOption.REPLACE_EXISTING); 84 String actualSha256 = computeSha256(targetPath); 85 if (!actualSha256.equals(expectedSha256)) { 86 throw new IllegalStateException( 87 "SHA256 expected=" + expectedSha256 + ", actual=" + actualSha256); 88 } 89 success = true; 90 } catch (IOException e) { 91 throw new IllegalStateException("Unable to copy resource from" 92 + " resource=" + resourceName 93 + " to targetPath=" + targetPath, e); 94 } finally { 95 if (!success) { 96 targetPath.delete(); 97 } 98 } 99 } 100 computeSha256(File file)101 private static String computeSha256(File file) { 102 try { 103 return com.google.common.io.Files.asByteSource(file).hash(Hashing.sha256()).toString(); 104 } catch (IOException e) { 105 throw new IllegalStateException("Unable to compute hash for file=" + file); 106 } 107 } 108 } 109