1 /* 2 * Copyright (C) 2019 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.util.mainline; 17 18 import android.content.pm.PackageInfo; 19 import android.content.pm.PackageManager; 20 import android.util.Log; 21 22 import java.security.MessageDigest; 23 import java.util.EnumSet; 24 import java.util.HashSet; 25 import java.util.Set; 26 27 /** 28 * Detects mainline modules. 29 */ 30 public class ModuleDetector { 31 private static final String LOG_TAG = "MainlineModuleDetector"; 32 33 private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); 34 35 /** 36 * Return true if a module is play managed. 37 * 38 * Example of skipping a test based on mainline modules: 39 * <pre> 40 * @Test 41 * public void testPocCVE_1234_5678() throws Exception { 42 * if(!ModuleDetector.moduleIsPlayManaged( 43 * getInstrumentation().getContext().getPackageManager(), 44 * MainlineModule.MEDIA_SOFTWARE_CODEC)) { 45 * doStagefrightTest(R.raw.cve_2018_5882); 46 * } 47 * } 48 * </pre> 49 */ moduleIsPlayManaged(PackageManager pm, MainlineModule module)50 public static boolean moduleIsPlayManaged(PackageManager pm, MainlineModule module) 51 throws Exception { 52 return getPlayManagedModules(pm).contains(module); 53 } 54 55 56 /** 57 * Return all play managed mainline modules. 58 */ getPlayManagedModules(PackageManager pm)59 public static Set<MainlineModule> getPlayManagedModules(PackageManager pm) throws Exception { 60 Set<MainlineModule> playManagedModules = new HashSet<>(); 61 62 Set<String> packages = new HashSet<>(); 63 for (PackageInfo info : pm.getInstalledPackages(0)) { 64 packages.add(info.packageName); 65 } 66 for (PackageInfo info : pm.getInstalledPackages(PackageManager.MATCH_APEX)) { 67 packages.add(info.packageName); 68 } 69 70 for (MainlineModule module : EnumSet.allOf(MainlineModule.class)) { 71 if (module.isPlayUpdated && packages.contains(module.packageName) 72 && module.certSHA256.equals(getSignatureDigest(pm, module))) { 73 playManagedModules.add(module); 74 } 75 } 76 return playManagedModules; 77 } 78 getSignatureDigest(PackageManager pm, MainlineModule module)79 private static String getSignatureDigest(PackageManager pm, MainlineModule module) 80 throws Exception { 81 int flag = PackageManager.GET_SIGNING_CERTIFICATES; 82 if (module.moduleType == ModuleType.APEX) { 83 flag |= PackageManager.MATCH_APEX; 84 } 85 86 PackageInfo packageInfo = pm.getPackageInfo(module.packageName, flag); 87 MessageDigest messageDigest = MessageDigest.getInstance("SHA256"); 88 messageDigest.update(packageInfo.signingInfo.getApkContentsSigners()[0].toByteArray()); 89 90 final byte[] digest = messageDigest.digest(); 91 final int digestLength = digest.length; 92 final int charCount = 3 * digestLength - 1; 93 94 final char[] chars = new char[charCount]; 95 for (int i = 0; i < digestLength; i++) { 96 final int byteHex = digest[i] & 0xFF; 97 chars[i * 3] = HEX_ARRAY[byteHex >>> 4]; 98 chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F]; 99 if (i < digestLength - 1) { 100 chars[i * 3 + 2] = ':'; 101 } 102 } 103 104 String ret = new String(chars); 105 Log.d(LOG_TAG, "Module: " + module.packageName + " has signature: " + ret); 106 return ret; 107 } 108 } 109