• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.messaging.util;
18 
19 import android.Manifest;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.os.Build;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 
26 import com.android.messaging.Factory;
27 
28 import java.util.ArrayList;
29 import java.util.Hashtable;
30 import java.util.Set;
31 
32 /**
33  * Android OS version utilities
34  */
35 public class OsUtil {
36     private static boolean sIsAtLeastICS_MR1;
37     private static boolean sIsAtLeastJB;
38     private static boolean sIsAtLeastJB_MR1;
39     private static boolean sIsAtLeastJB_MR2;
40     private static boolean sIsAtLeastKLP;
41     private static boolean sIsAtLeastL;
42     private static boolean sIsAtLeastL_MR1;
43     private static boolean sIsAtLeastM;
44 
45     private static Boolean sIsSecondaryUser = null;
46 
47     static {
48         final int v = getApiVersion();
49         sIsAtLeastICS_MR1 = v >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1;
50         sIsAtLeastJB = v >= android.os.Build.VERSION_CODES.JELLY_BEAN;
51         sIsAtLeastJB_MR1 = v >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
52         sIsAtLeastJB_MR2 = v >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
53         sIsAtLeastKLP = v >= android.os.Build.VERSION_CODES.KITKAT;
54         sIsAtLeastL = v >= android.os.Build.VERSION_CODES.LOLLIPOP;
55         sIsAtLeastL_MR1 = v >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
56         sIsAtLeastM = v >= android.os.Build.VERSION_CODES.M;
57     }
58 
59     /**
60      * @return True if the version of Android that we're running on is at least Ice Cream Sandwich
61      *  MR1 (API level 15).
62      */
isAtLeastICS_MR1()63     public static boolean isAtLeastICS_MR1() {
64         return sIsAtLeastICS_MR1;
65     }
66 
67     /**
68      * @return True if the version of Android that we're running on is at least Jelly Bean
69      *  (API level 16).
70      */
isAtLeastJB()71     public static boolean isAtLeastJB() {
72         return sIsAtLeastJB;
73     }
74 
75     /**
76      * @return True if the version of Android that we're running on is at least Jelly Bean MR1
77      *  (API level 17).
78      */
isAtLeastJB_MR1()79     public static boolean isAtLeastJB_MR1() {
80         return sIsAtLeastJB_MR1;
81     }
82 
83     /**
84      * @return True if the version of Android that we're running on is at least Jelly Bean MR2
85      *  (API level 18).
86      */
isAtLeastJB_MR2()87     public static boolean isAtLeastJB_MR2() {
88         return sIsAtLeastJB_MR2;
89     }
90 
91     /**
92      * @return True if the version of Android that we're running on is at least KLP
93      *  (API level 19).
94      */
isAtLeastKLP()95     public static boolean isAtLeastKLP() {
96         return sIsAtLeastKLP;
97     }
98 
99     /**
100      * @return True if the version of Android that we're running on is at least L
101      *  (API level 21).
102      */
isAtLeastL()103     public static boolean isAtLeastL() {
104         return sIsAtLeastL;
105     }
106 
107     /**
108      * @return True if the version of Android that we're running on is at least L MR1
109      *  (API level 22).
110      */
isAtLeastL_MR1()111     public static boolean isAtLeastL_MR1() {
112         return sIsAtLeastL_MR1;
113     }
114 
115     /**
116      * @return True if the version of Android that we're running on is at least M
117      *  (API level 23).
118      */
isAtLeastM()119     public static boolean isAtLeastM() {
120         return sIsAtLeastM;
121     }
122 
123     /**
124      * @return The Android API version of the OS that we're currently running on.
125      */
getApiVersion()126     public static int getApiVersion() {
127         return android.os.Build.VERSION.SDK_INT;
128     }
129 
isSecondaryUser()130     public static boolean isSecondaryUser() {
131         if (sIsSecondaryUser == null) {
132             final Context context = Factory.get().getApplicationContext();
133             boolean isSecondaryUser = false;
134 
135             // Only check for newer devices (but not the nexus 10)
136             if (OsUtil.sIsAtLeastJB_MR1 && !"Nexus 10".equals(Build.MODEL)) {
137                 final UserHandle uh = android.os.Process.myUserHandle();
138                 final UserManager userManager =
139                         (UserManager) context.getSystemService(Context.USER_SERVICE);
140                 if (userManager != null) {
141                     final long userSerialNumber = userManager.getSerialNumberForUser(uh);
142                     isSecondaryUser = (0 != userSerialNumber);
143                 }
144             }
145             sIsSecondaryUser = isSecondaryUser;
146         }
147         return sIsSecondaryUser;
148     }
149 
150     /**
151      * Creates a joined string from a Set<String> using the given delimiter.
152      * @param values
153      * @param delimiter
154      * @return
155      */
joinFromSetWithDelimiter( final Set<String> values, final String delimiter)156     public static String joinFromSetWithDelimiter(
157             final Set<String> values, final String delimiter) {
158         if (values != null) {
159             final StringBuilder joinedStringBuilder = new StringBuilder();
160             boolean firstValue = true;
161             for (final String value : values) {
162                 if (firstValue) {
163                     firstValue = false;
164                 } else {
165                     joinedStringBuilder.append(delimiter);
166                 }
167                 joinedStringBuilder.append(value);
168             }
169             return joinedStringBuilder.toString();
170         }
171         return null;
172     }
173 
174     private static Hashtable<String, Integer> sPermissions = new Hashtable<String, Integer>();
175 
176     /**
177      * Check if the app has the specified permission. If it does not, the app needs to use
178      * {@link android.app.Activity#requestPermission}. Note that if it
179      * returns true, it cannot return false in the same process as the OS kills the process when
180      * any permission is revoked.
181      * @param permission A permission from {@link android.Manifest.permission}
182      */
hasPermission(final String permission)183     public static boolean hasPermission(final String permission) {
184         if (OsUtil.isAtLeastM()) {
185             // It is safe to cache the PERMISSION_GRANTED result as the process gets killed if the
186             // user revokes the permission setting. However, PERMISSION_DENIED should not be
187             // cached as the process does not get killed if the user enables the permission setting.
188             if (!sPermissions.containsKey(permission)
189                     || sPermissions.get(permission) == PackageManager.PERMISSION_DENIED) {
190                 final Context context = Factory.get().getApplicationContext();
191                 final int permissionState = context.checkSelfPermission(permission);
192                 sPermissions.put(permission, permissionState);
193             }
194             return sPermissions.get(permission) == PackageManager.PERMISSION_GRANTED;
195         } else {
196             return true;
197         }
198     }
199 
200     /** Does the app have all the specified permissions */
hasPermissions(final String[] permissions)201     public static boolean hasPermissions(final String[] permissions) {
202         for (final String permission : permissions) {
203             if (!hasPermission(permission)) {
204                 return false;
205             }
206         }
207         return true;
208     }
209 
hasPhonePermission()210     public static boolean hasPhonePermission() {
211         return hasPermission(Manifest.permission.READ_PHONE_STATE);
212     }
213 
hasSmsPermission()214     public static boolean hasSmsPermission() {
215         return hasPermission(Manifest.permission.READ_SMS);
216     }
217 
hasLocationPermission()218     public static boolean hasLocationPermission() {
219         return OsUtil.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION);
220     }
221 
222 
hasStoragePermission()223     public static boolean hasStoragePermission() {
224         // Note that READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE are granted or denied
225         // together.
226         return OsUtil.hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
227     }
228 
hasRecordAudioPermission()229     public static boolean hasRecordAudioPermission() {
230         return OsUtil.hasPermission(Manifest.permission.RECORD_AUDIO);
231     }
232 
233     /**
234      * Returns array with the set of permissions that have not been granted from the given set.
235      * The array will be empty if the app has all of the specified permissions. Note that calling
236      * {@link Activity#requestPermissions} for an already granted permission can prompt the user
237      * again, and its up to the app to only request permissions that are missing.
238      */
getMissingPermissions(final String[] permissions)239     public static String[] getMissingPermissions(final String[] permissions) {
240         final ArrayList<String> missingList = new ArrayList<String>();
241         for (final String permission : permissions) {
242             if (!hasPermission(permission)) {
243                 missingList.add(permission);
244             }
245         }
246 
247         final String[] missingArray = new String[missingList.size()];
248         missingList.toArray(missingArray);
249         return missingArray;
250     }
251 
252     private static String[] sRequiredPermissions = new String[] {
253         // Required to read existing SMS threads
254         Manifest.permission.READ_SMS,
255         // Required for knowing the phone number, number of SIMs, etc.
256         Manifest.permission.READ_PHONE_STATE,
257         // This is not strictly required, but simplifies the contact picker scenarios
258         Manifest.permission.READ_CONTACTS,
259     };
260 
261     /** Does the app have the minimum set of permissions required to operate. */
hasRequiredPermissions()262     public static boolean hasRequiredPermissions() {
263         return hasPermissions(sRequiredPermissions);
264     }
265 
getMissingRequiredPermissions()266     public static String[] getMissingRequiredPermissions() {
267         return getMissingPermissions(sRequiredPermissions);
268     }
269 }
270