Home | History | Annotate | Download | only in util
      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      */
     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      */
     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      */
     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      */
     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      */
     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      */
    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      */
    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      */
    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      */
    126     public static int getApiVersion() {
    127         return android.os.Build.VERSION.SDK_INT;
    128     }
    129 
    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      */
    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      */
    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 */
    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 
    210     public static boolean hasPhonePermission() {
    211         return hasPermission(Manifest.permission.READ_PHONE_STATE);
    212     }
    213 
    214     public static boolean hasSmsPermission() {
    215         return hasPermission(Manifest.permission.READ_SMS);
    216     }
    217 
    218     public static boolean hasLocationPermission() {
    219         return OsUtil.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION);
    220     }
    221 
    222 
    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 
    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      */
    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. */
    262     public static boolean hasRequiredPermissions() {
    263         return hasPermissions(sRequiredPermissions);
    264     }
    265 
    266     public static String[] getMissingRequiredPermissions() {
    267         return getMissingPermissions(sRequiredPermissions);
    268     }
    269 }
    270