• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.compatibility.common.util;
18 
19 import android.content.pm.ApplicationInfo;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageManager;
22 import android.util.Log;
23 
24 import androidx.test.InstrumentationRegistry;
25 
26 import java.security.MessageDigest;
27 import java.security.NoSuchAlgorithmException;
28 
29 /**
30  * Device-side utility class for PackageManager-related operations
31  */
32 public class PackageUtil {
33 
34     private static final String TAG = PackageUtil.class.getSimpleName();
35 
36     private static final int SYSTEM_APP_MASK =
37             ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
38     private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
39 
40     /** Returns true if a package with the given name exists on the device */
exists(String packageName)41     public static boolean exists(String packageName) {
42         try {
43             return (getPackageManager().getApplicationInfo(packageName,
44                     PackageManager.GET_META_DATA) != null);
45         } catch(PackageManager.NameNotFoundException e) {
46             return false;
47         }
48     }
49 
50     /** Returns true if a package with the given name AND SHA digest exists on the device */
exists(String packageName, String sha)51     public static boolean exists(String packageName, String sha) {
52         try {
53             if (getPackageManager().getApplicationInfo(
54                     packageName, PackageManager.GET_META_DATA) == null) {
55                 return false;
56             }
57             return sha.equals(computePackageSignatureDigest(packageName));
58         } catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException e) {
59             return false;
60         }
61     }
62 
63     /** Returns true if the app for the given package name is a system app for this device */
isSystemApp(String packageName)64     public static boolean isSystemApp(String packageName) {
65         try {
66             ApplicationInfo ai = getPackageManager().getApplicationInfo(packageName,
67                     PackageManager.GET_META_DATA);
68             return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0);
69         } catch(PackageManager.NameNotFoundException e) {
70             return false;
71         }
72     }
73 
74     /**
75      * Returns true if the app for the given package name is a privileged system app for this
76      * device
77      */
isPrivilegedSystemApp(String packageName)78     public static boolean isPrivilegedSystemApp(String packageName) {
79         try {
80             ApplicationInfo ai = getPackageManager().getApplicationInfo(packageName,
81                     PackageManager.GET_META_DATA);
82             return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0) && ai.isPrivilegedApp();
83         } catch(PackageManager.NameNotFoundException e) {
84             return false;
85         }
86     }
87 
88     /** Returns the version string of the package name, or null if the package can't be found */
getVersionString(String packageName)89     public static String getVersionString(String packageName) {
90         try {
91             PackageInfo info = getPackageManager().getPackageInfo(packageName,
92                     PackageManager.GET_META_DATA);
93             return info.versionName;
94         } catch (PackageManager.NameNotFoundException | NullPointerException e) {
95             Log.w(TAG, "Could not find version string for package " + packageName);
96             return null;
97         }
98     }
99 
100     /**
101      * Compute the signature SHA digest for a package.
102      * @param package the name of the package for which the signature SHA digest is requested
103      * @return the signature SHA digest
104      */
computePackageSignatureDigest(String packageName)105     public static String computePackageSignatureDigest(String packageName)
106             throws NoSuchAlgorithmException, PackageManager.NameNotFoundException {
107         PackageInfo packageInfo = getPackageManager()
108                 .getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
109         MessageDigest messageDigest = MessageDigest.getInstance("SHA256");
110         messageDigest.update(packageInfo.signatures[0].toByteArray());
111 
112         final byte[] digest = messageDigest.digest();
113         final int digestLength = digest.length;
114         final int charCount = 3 * digestLength - 1;
115 
116         final char[] chars = new char[charCount];
117         for (int i = 0; i < digestLength; i++) {
118             final int byteHex = digest[i] & 0xFF;
119             chars[i * 3] = HEX_ARRAY[byteHex >>> 4];
120             chars[i * 3 + 1] = HEX_ARRAY[byteHex & 0x0F];
121             if (i < digestLength - 1) {
122                 chars[i * 3 + 2] = ':';
123             }
124         }
125         return new String(chars);
126     }
127 
getPackageManager()128     private static PackageManager getPackageManager() {
129         return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
130     }
131 
hasDeviceFeature(final String requiredFeature)132     private static boolean hasDeviceFeature(final String requiredFeature) {
133         return InstrumentationRegistry.getContext()
134                 .getPackageManager()
135                 .hasSystemFeature(requiredFeature);
136     }
137 
138     /**
139      * Rotation support is indicated by explicitly having both landscape and portrait
140      * features or not listing either at all.
141      */
supportsRotation()142     public static boolean supportsRotation() {
143         final boolean supportsLandscape = hasDeviceFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
144         final boolean supportsPortrait = hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT);
145         return (supportsLandscape && supportsPortrait)
146                 || (!supportsLandscape && !supportsPortrait);
147     }
148 }
149