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