diff options
-rw-r--r-- | tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java | 330 |
1 files changed, 0 insertions, 330 deletions
diff --git a/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java b/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java deleted file mode 100644 index 05a86c96ea..0000000000 --- a/tzdata/prototype_data/src/libcore/tzdata/prototypedata/TimeZoneRulesDataProvider.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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 libcore.tzdata.prototypedata; - -import com.android.timezone.distro.DistroException; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.TimeZoneDistro; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.pm.ProviderInfo; -import android.content.res.AssetManager; -import android.database.AbstractCursor; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.provider.TimeZoneRulesDataContract; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static android.content.res.AssetManager.ACCESS_STREAMING; -import static android.provider.TimeZoneRulesDataContract.COLUMN_DISTRO_MAJOR_VERSION; -import static android.provider.TimeZoneRulesDataContract.COLUMN_DISTRO_MINOR_VERSION; -import static android.provider.TimeZoneRulesDataContract.COLUMN_OPERATION; -import static android.provider.TimeZoneRulesDataContract.COLUMN_REVISION; -import static android.provider.TimeZoneRulesDataContract.COLUMN_RULES_VERSION; -import static android.provider.TimeZoneRulesDataContract.OPERATION_INSTALL; - -/** - * A basic implementation of a time zone data provider that can be used by OEMs to implement - * an APK asset-based solution for time zone updates. - */ -public final class TimeZoneRulesDataProvider extends ContentProvider { - - static final String TAG = "TimeZoneRulesDataProvider"; - - private static final String METADATA_KEY_OPERATION = "android.timezoneprovider.OPERATION"; - - private static final Set<String> KNOWN_COLUMN_NAMES; - private static final Map<String, Class<?>> KNOWN_COLUMN_TYPES; - - static { - Set<String> columnNames = new HashSet<>(); - columnNames.add(COLUMN_OPERATION); - columnNames.add(COLUMN_DISTRO_MAJOR_VERSION); - columnNames.add(COLUMN_DISTRO_MINOR_VERSION); - columnNames.add(COLUMN_RULES_VERSION); - columnNames.add(COLUMN_REVISION); - KNOWN_COLUMN_NAMES = Collections.unmodifiableSet(columnNames); - - Map<String, Class<?>> columnTypes = new HashMap<>(); - columnTypes.put(COLUMN_OPERATION, String.class); - columnTypes.put(COLUMN_DISTRO_MAJOR_VERSION, Integer.class); - columnTypes.put(COLUMN_DISTRO_MINOR_VERSION, Integer.class); - columnTypes.put(COLUMN_RULES_VERSION, String.class); - columnTypes.put(COLUMN_REVISION, Integer.class); - KNOWN_COLUMN_TYPES = Collections.unmodifiableMap(columnTypes); - } - - private final Map<String, Object> mColumnData = new HashMap<>(); - - @Override - public boolean onCreate() { - return true; - } - - @Override - public void attachInfo(Context context, ProviderInfo info) { - super.attachInfo(context, info); - - // Sanity check our security - if (!TimeZoneRulesDataContract.AUTHORITY.equals(info.authority)) { - // The authority looked for by the time zone updater is fixed. - throw new SecurityException( - "android:authorities must be \"" + TimeZoneRulesDataContract.AUTHORITY + "\""); - } - if (!info.grantUriPermissions) { - throw new SecurityException("Provider must grant uri permissions"); - } - if (!info.exported) { - // The content provider is accessed directly so must be exported. - throw new SecurityException("android:exported must be \"true\""); - } - if (info.pathPermissions != null || info.writePermission != null) { - // Use readPermission only to implement permissions. - throw new SecurityException("Use android:readPermission only"); - } - if (!android.Manifest.permission.UPDATE_TIME_ZONE_RULES.equals(info.readPermission)) { - // Writing is not supported. - throw new SecurityException("android:readPermission must be set to \"" - + android.Manifest.permission.UPDATE_TIME_ZONE_RULES - + "\" is: " + info.readPermission); - } - - // info.metadata is not filled in by default. Must ask for it again. - final ProviderInfo infoWithMetadata = context.getPackageManager() - .resolveContentProvider(info.authority, PackageManager.GET_META_DATA); - Bundle metaData = infoWithMetadata.metaData; - if (metaData == null) { - throw new SecurityException("meta-data must be set"); - } - - // Work out what the operation is. - String operation; - try { - operation = getMandatoryMetaDataString(metaData, METADATA_KEY_OPERATION); - mColumnData.put(COLUMN_OPERATION, operation); - } catch (IllegalArgumentException e) { - throw new SecurityException(METADATA_KEY_OPERATION + " meta-data not set."); - } - - // Fill in version information if this is an install operation. - if (OPERATION_INSTALL.equals(operation)) { - // Extract the version information from the distro. - InputStream distroBytesInputStream; - try { - distroBytesInputStream = context.getAssets().open(TimeZoneDistro.FILE_NAME); - } catch (IOException e) { - throw new SecurityException( - "Unable to open asset: " + TimeZoneDistro.FILE_NAME, e); - } - TimeZoneDistro distro = new TimeZoneDistro(distroBytesInputStream); - try { - DistroVersion distroVersion = distro.getDistroVersion(); - mColumnData.put(COLUMN_DISTRO_MAJOR_VERSION, distroVersion.formatMajorVersion); - mColumnData.put(COLUMN_DISTRO_MINOR_VERSION, distroVersion.formatMinorVersion); - mColumnData.put(COLUMN_RULES_VERSION, distroVersion.rulesVersion); - mColumnData.put(COLUMN_REVISION, distroVersion.revision); - } catch (IOException | DistroException e) { - throw new SecurityException("Invalid asset: " + TimeZoneDistro.FILE_NAME, e); - } - - } - } - - @Override - public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, - @Nullable String[] selectionArgs, @Nullable String sortOrder) { - if (!TimeZoneRulesDataContract.OPERATION_URI.equals(uri)) { - return null; - } - final List<String> projectionList = Arrays.asList(projection); - if (projection != null && !KNOWN_COLUMN_NAMES.containsAll(projectionList)) { - throw new UnsupportedOperationException( - "Only " + KNOWN_COLUMN_NAMES + " columns supported."); - } - - return new AbstractCursor() { - @Override - public int getCount() { - return 1; - } - - @Override - public String[] getColumnNames() { - return projectionList.toArray(new String[0]); - } - - @Override - public int getType(int column) { - String columnName = projectionList.get(column); - Class<?> columnJavaType = KNOWN_COLUMN_TYPES.get(columnName); - if (columnJavaType == String.class) { - return Cursor.FIELD_TYPE_STRING; - } else if (columnJavaType == Integer.class) { - return Cursor.FIELD_TYPE_INTEGER; - } else { - throw new UnsupportedOperationException( - "Unsupported type: " + columnJavaType + " for " + columnName); - } - } - - @Override - public String getString(int column) { - checkPosition(); - String columnName = projectionList.get(column); - if (KNOWN_COLUMN_TYPES.get(columnName) != String.class) { - throw new UnsupportedOperationException(); - } - return (String) mColumnData.get(columnName); - } - - @Override - public short getShort(int column) { - checkPosition(); - throw new UnsupportedOperationException(); - } - - @Override - public int getInt(int column) { - checkPosition(); - String columnName = projectionList.get(column); - if (KNOWN_COLUMN_TYPES.get(columnName) != Integer.class) { - throw new UnsupportedOperationException(); - } - return (Integer) mColumnData.get(columnName); - } - - @Override - public long getLong(int column) { - return getInt(column); - } - - @Override - public float getFloat(int column) { - throw new UnsupportedOperationException(); - } - - @Override - public double getDouble(int column) { - checkPosition(); - throw new UnsupportedOperationException(); - } - - @Override - public boolean isNull(int column) { - checkPosition(); - return column != 0; - } - }; - } - - @Override - public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) - throws FileNotFoundException { - if (!TimeZoneRulesDataContract.DATA_URI.equals(uri)) { - throw new FileNotFoundException("Unknown URI: " + uri); - } - if (!"r".equals(mode)) { - throw new FileNotFoundException("Only read-only access supported."); - } - - // We cannot return the asset ParcelFileDescriptor from - // assets.openFd(name).getParcelFileDescriptor() here as the receiver in the reading - // process gets a ParcelFileDescriptor pointing at the whole .apk. Instead, we extract - // the asset file we want to storage then wrap that in a ParcelFileDescriptor. - File distroFile = null; - try { - distroFile = File.createTempFile("distro", null, getContext().getFilesDir()); - - AssetManager assets = getContext().getAssets(); - try (InputStream is = assets.open(TimeZoneDistro.FILE_NAME, ACCESS_STREAMING); - FileOutputStream fos = new FileOutputStream(distroFile, false /* append */)) { - copy(is, fos); - } - - return ParcelFileDescriptor.open(distroFile, ParcelFileDescriptor.MODE_READ_ONLY); - } catch (IOException e) { - throw new RuntimeException("Unable to copy distro asset file", e); - } finally { - if (distroFile != null) { - // Even if we have an open file descriptor pointing at the file it should be safe to - // delete because of normal Unix file behavior. Deleting here avoids leaking any - // storage. - distroFile.delete(); - } - } - } - - @Override - public String getType(@NonNull Uri uri) { - return null; - } - - @Override - public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { - throw new UnsupportedOperationException(); - } - - @Override - public int delete(@NonNull Uri uri, @Nullable String selection, - @Nullable String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - - @Override - public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, - @Nullable String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - - private static String getMandatoryMetaDataString(Bundle metaData, String key) { - if (!metaData.containsKey(key)) { - throw new SecurityException("No metadata with key " + key + " found."); - } - return metaData.getString(key); - } - - /** - * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed. - */ - private static void copy(InputStream in, OutputStream out) throws IOException { - byte[] buffer = new byte[8192]; - int c; - while ((c = in.read(buffer)) != -1) { - out.write(buffer, 0, c); - } - } -} |