1 /* 2 * Copyright (C) 2017 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 17 package com.android.dialer.about; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import com.android.dialer.common.Assert; 22 import java.io.ByteArrayOutputStream; 23 import java.io.IOException; 24 import java.io.InputStream; 25 import java.io.UnsupportedEncodingException; 26 import java.util.ArrayList; 27 import java.util.Collections; 28 29 /** A helper for extracting licenses. */ 30 public final class Licenses { 31 private static final String TAG = "Licenses"; 32 private static final String LICENSE_FILENAME = "third_party_licenses"; 33 private static final String LICENSE_METADATA_FILENAME = "third_party_license_metadata"; 34 35 /** Return the licenses bundled into this app. */ 36 public static ArrayList<License> getLicenses(Context context) { 37 return getLicenseListFromMetadata( 38 getTextFromResource(context.getApplicationContext(), LICENSE_METADATA_FILENAME, 0, -1)); 39 } 40 41 /** 42 * Returns a list of {@link License}s parsed from a license metadata file. 43 * 44 * @param metadata a {@code String} containing the contents of a license metadata file. 45 */ 46 private static ArrayList<License> getLicenseListFromMetadata(String metadata) { 47 String[] entries = metadata.split("\n"); 48 ArrayList<License> licenses = new ArrayList<License>(entries.length); 49 for (String entry : entries) { 50 int delimiter = entry.indexOf(' '); 51 String[] licenseLocation = entry.substring(0, delimiter).split(":"); 52 Assert.checkState( 53 delimiter > 0 && licenseLocation.length == 2, 54 "Invalid license meta-data line:\n" + entry); 55 long licenseOffset = Long.parseLong(licenseLocation[0]); 56 int licenseLength = Integer.parseInt(licenseLocation[1]); 57 licenses.add(License.create(entry.substring(delimiter + 1), licenseOffset, licenseLength)); 58 } 59 Collections.sort(licenses); 60 return licenses; 61 } 62 63 /** Return the text of a bundled license file. */ 64 public static String getLicenseText(Context context, License license) { 65 long offset = license.getLicenseOffset(); 66 int length = license.getLicenseLength(); 67 return getTextFromResource(context, LICENSE_FILENAME, offset, length); 68 } 69 70 private static String getTextFromResource( 71 Context context, String filename, long offset, int length) { 72 Resources resources = context.getApplicationContext().getResources(); 73 // When aapt is called with --rename-manifest-package, the package name is changed for the 74 // application, but not for the resources. This is to find the package name of a known 75 // resource to know what package to lookup the license files in. 76 String packageName = resources.getResourcePackageName(R.id.dummy_placeholder); 77 InputStream stream = 78 resources.openRawResource(resources.getIdentifier(filename, "raw", packageName)); 79 return getTextFromInputStream(stream, offset, length); 80 } 81 82 private static String getTextFromInputStream(InputStream stream, long offset, int length) { 83 byte[] buffer = new byte[1024]; 84 ByteArrayOutputStream textArray = new ByteArrayOutputStream(); 85 86 try { 87 stream.skip(offset); 88 int bytesRemaining = length > 0 ? length : Integer.MAX_VALUE; 89 int bytes = 0; 90 91 while (bytesRemaining > 0 92 && (bytes = stream.read(buffer, 0, Math.min(bytesRemaining, buffer.length))) != -1) { 93 textArray.write(buffer, 0, bytes); 94 bytesRemaining -= bytes; 95 } 96 stream.close(); 97 } catch (IOException e) { 98 throw new RuntimeException("Failed to read license or metadata text.", e); 99 } 100 try { 101 return textArray.toString("UTF-8"); 102 } catch (UnsupportedEncodingException e) { 103 throw new RuntimeException("Unsupported encoding UTF8. This should always be supported.", e); 104 } 105 } 106 } 107