1 /* 2 * Copyright (C) 2021 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.server.pm; 18 19 import android.annotation.NonNull; 20 import android.text.TextUtils; 21 22 import com.android.internal.util.HexDump; 23 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.List; 27 28 class PerPackageReadTimeouts { tryParseLong(String str, long defaultValue)29 static long tryParseLong(String str, long defaultValue) { 30 try { 31 return Long.parseLong(str); 32 } catch (NumberFormatException nfe) { 33 return defaultValue; 34 } 35 } 36 tryParseSha256(String str)37 static byte[] tryParseSha256(String str) { 38 if (TextUtils.isEmpty(str)) { 39 return null; 40 } 41 try { 42 return HexDump.hexStringToByteArray(str); 43 } catch (RuntimeException e) { 44 return null; 45 } 46 } 47 48 static class Timeouts { 49 public final long minTimeUs; 50 public final long minPendingTimeUs; 51 public final long maxPendingTimeUs; 52 53 // 3600000000us == 1hr 54 public static final Timeouts DEFAULT = new Timeouts(3600000000L, 3600000000L, 3600000000L); 55 Timeouts(long minTimeUs, long minPendingTimeUs, long maxPendingTimeUs)56 private Timeouts(long minTimeUs, long minPendingTimeUs, long maxPendingTimeUs) { 57 this.minTimeUs = minTimeUs; 58 this.minPendingTimeUs = minPendingTimeUs; 59 this.maxPendingTimeUs = maxPendingTimeUs; 60 } 61 parse(String timeouts)62 static Timeouts parse(String timeouts) { 63 String[] splits = timeouts.split(":", 3); 64 if (splits.length != 3) { 65 return DEFAULT; 66 } 67 final long minTimeUs = tryParseLong(splits[0], DEFAULT.minTimeUs); 68 final long minPendingTimeUs = tryParseLong(splits[1], DEFAULT.minPendingTimeUs); 69 final long maxPendingTimeUs = tryParseLong(splits[2], DEFAULT.maxPendingTimeUs); 70 if (0 <= minTimeUs && minTimeUs <= minPendingTimeUs 71 && minPendingTimeUs <= maxPendingTimeUs) { 72 // validity check 73 return new Timeouts(minTimeUs, minPendingTimeUs, maxPendingTimeUs); 74 } 75 return DEFAULT; 76 } 77 } 78 79 static class VersionCodes { 80 public final long minVersionCode; 81 public final long maxVersionCode; 82 83 public static final VersionCodes ALL_VERSION_CODES = new VersionCodes(Long.MIN_VALUE, 84 Long.MAX_VALUE); 85 VersionCodes(long minVersionCode, long maxVersionCode)86 private VersionCodes(long minVersionCode, long maxVersionCode) { 87 this.minVersionCode = minVersionCode; 88 this.maxVersionCode = maxVersionCode; 89 } 90 parse(String codes)91 static VersionCodes parse(String codes) { 92 if (TextUtils.isEmpty(codes)) { 93 return ALL_VERSION_CODES; 94 } 95 String[] splits = codes.split("-", 2); 96 switch (splits.length) { 97 case 1: { 98 // single version code 99 try { 100 final long versionCode = Long.parseLong(splits[0]); 101 return new VersionCodes(versionCode, versionCode); 102 } catch (NumberFormatException nfe) { 103 return ALL_VERSION_CODES; 104 } 105 } 106 case 2: { 107 final long minVersionCode = tryParseLong(splits[0], 108 ALL_VERSION_CODES.minVersionCode); 109 final long maxVersionCode = tryParseLong(splits[1], 110 ALL_VERSION_CODES.maxVersionCode); 111 if (minVersionCode <= maxVersionCode) { 112 return new VersionCodes(minVersionCode, maxVersionCode); 113 } 114 break; 115 } 116 } 117 return ALL_VERSION_CODES; 118 } 119 } 120 121 public final String packageName; 122 public final byte[] sha256certificate; 123 public final VersionCodes versionCodes; 124 public final Timeouts timeouts; 125 PerPackageReadTimeouts(String packageName, byte[] sha256certificate, VersionCodes versionCodes, Timeouts timeouts)126 private PerPackageReadTimeouts(String packageName, byte[] sha256certificate, 127 VersionCodes versionCodes, Timeouts timeouts) { 128 this.packageName = packageName; 129 this.sha256certificate = sha256certificate; 130 this.versionCodes = versionCodes; 131 this.timeouts = timeouts; 132 } 133 134 @SuppressWarnings("fallthrough") parse(String timeoutsStr, VersionCodes defaultVersionCodes, Timeouts defaultTimeouts)135 static PerPackageReadTimeouts parse(String timeoutsStr, VersionCodes defaultVersionCodes, 136 Timeouts defaultTimeouts) { 137 String packageName = null; 138 byte[] sha256certificate = null; 139 VersionCodes versionCodes = defaultVersionCodes; 140 Timeouts timeouts = defaultTimeouts; 141 142 final String[] splits = timeoutsStr.split(":", 4); 143 switch (splits.length) { 144 case 4: 145 timeouts = Timeouts.parse(splits[3]); 146 // fall through 147 case 3: 148 versionCodes = VersionCodes.parse(splits[2]); 149 // fall through 150 case 2: 151 sha256certificate = tryParseSha256(splits[1]); 152 // fall through 153 case 1: 154 packageName = splits[0]; 155 break; 156 default: 157 return null; 158 } 159 if (TextUtils.isEmpty(packageName)) { 160 return null; 161 } 162 163 return new PerPackageReadTimeouts(packageName, sha256certificate, versionCodes, 164 timeouts); 165 } 166 parseDigestersList(String defaultTimeoutsStr, String knownDigestersList)167 static @NonNull List<PerPackageReadTimeouts> parseDigestersList(String defaultTimeoutsStr, 168 String knownDigestersList) { 169 if (TextUtils.isEmpty(knownDigestersList)) { 170 return Collections.emptyList(); 171 } 172 173 final VersionCodes defaultVersionCodes = VersionCodes.ALL_VERSION_CODES; 174 final Timeouts defaultTimeouts = Timeouts.parse(defaultTimeoutsStr); 175 176 String[] packages = knownDigestersList.split(","); 177 List<PerPackageReadTimeouts> result = new ArrayList<>(packages.length); 178 for (int i = 0, size = packages.length; i < size; ++i) { 179 PerPackageReadTimeouts timeouts = PerPackageReadTimeouts.parse(packages[i], 180 defaultVersionCodes, defaultTimeouts); 181 if (timeouts != null) { 182 result.add(timeouts); 183 } 184 } 185 return result; 186 } 187 } 188