判斷是否獲取了懸浮窗權限

現在很多應用都會用到懸浮窗,很多國產rom把懸浮窗權限加入控制了,你就需要判斷是否有懸浮窗權限,然後做對應操作。

Android 原生有自帶權限管理的,只是被隱藏了。看android源碼在android.app下就有個AppOpsManager類。

類說明如下:

/**
 * API for interacting with "application operation" tracking.
 *
 * <p>This API is not generally intended for third party application developers; most
 * features are only available to system applications.  Obtain an instance of it through
 * {@link Context#getSystemService(String) Context.getSystemService} with
 * {@link Context#APP_OPS_SERVICE Context.APP_OPS_SERVICE}.</p>
 */
上面說明了只對系統應用有用,rom廠商們應該就是利用這個AppOps機制開放一些權限控制。

我們要判斷是否有權限該如何做呢?就只能通過反射去判斷了。


AppOpsManager的checkOp方法,就是檢測是否有某項權限的方法有這些返回值,分別是允許,忽略,錯誤和默認:

/**
 * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is
 * allowed to perform the given operation.
 */
public static final int MODE_ALLOWED = 0;

/**
 * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller is
 * not allowed to perform the given operation, and this attempt should
 * <em>silently fail</em> (it should not cause the app to crash).
 */
public static final int MODE_IGNORED = 1;

/**
 * Result from {@link #checkOpNoThrow}, {@link #noteOpNoThrow}, {@link #startOpNoThrow}: the
 * given caller is not allowed to perform the given operation, and this attempt should
 * cause it to have a fatal error, typically a {@link SecurityException}.
 */
public static final int MODE_ERRORED = 2;

/**
 * Result from {@link #checkOp}, {@link #noteOp}, {@link #startOp}: the given caller should
 * use its default security check.  This mode is not normally used; it should only be used
 * with appop permissions, and callers must explicitly check for it and deal with it.
 */
public static final int MODE_DEFAULT = 3;
只有MODE_ALLOWED纔是確定有權限的。


類裏面checkOp方法如下,三個參數分別是操作id,uid和包名:

/**
 * Do a quick check for whether an application might be able to perform an operation.
 * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
 * or {@link #startOp(int, int, String)} for your actual security checks, which also
 * ensure that the given uid and package name are consistent.  This function can just be
 * used for a quick check to see if an operation has been disabled for the application,
 * as an early reject of some work.  This does not modify the time stamp or other data
 * about the operation.
 * @param op The operation to check.  One of the OP_* constants.
 * @param uid The user id of the application attempting to perform the operation.
 * @param packageName The name of the application attempting to perform the operation.
 * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
 * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
 * causing the app to crash).
 * @throws SecurityException If the app has been configured to crash on this op.
 * @hide
 */
public int checkOp(int op, int uid, String packageName) {
    try {
        int mode = mService.checkOperation(op, uid, packageName);
        if (mode == MODE_ERRORED) {
            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
        }
        return mode;
    } catch (RemoteException e) {
    }
    return MODE_IGNORED;
}
操作id即op可以在該類中找到靜態值定義,android23裏面有62種權限,我們需要的是OP_SYSTEM_ALERT_WINDOW=24


知道這些就可以用反射把我們的方法寫出了:


[java] view plain copy
  1. /** 
  2.     * 判斷 懸浮窗口權限是否打開 
  3.     * 
  4.     * @param context 
  5.     * @return true 允許  false禁止 
  6.     */  
  7.    public static boolean getAppOps(Context context) {  
  8.        try {  
  9.            Object object = context.getSystemService("appops");  
  10.            if (object == null) {  
  11.                return false;  
  12.            }  
  13.            Class localClass = object.getClass();  
  14.            Class[] arrayOfClass = new Class[3];  
  15.            arrayOfClass[0] = Integer.TYPE;  
  16.            arrayOfClass[1] = Integer.TYPE;  
  17.            arrayOfClass[2] = String.class;  
  18.            Method method = localClass.getMethod("checkOp", arrayOfClass);  
  19.            if (method == null) {  
  20.                return false;  
  21.            }  
  22.            Object[] arrayOfObject1 = new Object[3];  
  23.            arrayOfObject1[0] = Integer.valueOf(24);  
  24.            arrayOfObject1[1] = Integer.valueOf(Binder.getCallingUid());  
  25.            arrayOfObject1[2] = context.getPackageName();  
  26.            int m = ((Integer) method.invoke(object, arrayOfObject1)).intValue();  
  27.            return m == AppOpsManager.MODE_ALLOWED;  
  28.        } catch (Exception ex) {  
  29.   
  30.        }  
  31.        return false;  
  32.    }  


測試在魅族華爲小米大部分機型上都是可以的,但這個方法也不能保證正確,一些機型上會返回錯誤即MODE_ERRORED,就是獲取不到權限值,這個方法就返回了false,但實際上懸浮窗是可以使用的。








第二篇針對MIUI:

  1. /**  
  2. * Created by chenzy on 2015/3/31.  
  3. *  
  4. * MIUI 懸浮窗判斷工具類  
  5. */  
  6. public class AlterWindowUtil {  
  7.     public static final String TAG ="AlterWindowUtil";  
  8.   
  9.     /**  
  10.      * 4.4 以上可以直接判斷準確  
  11.      *  
  12.      * 4.4 以下非MIUI直接返回true  
  13.      *  
  14.      * 4.4 以下MIUI 可 判斷 上一次打開app 時 是否開啓了懸浮窗權限  
  15.      *  
  16.      * @param context  
  17.      * @return  
  18.      */  
  19.     @TargetApi(Build.VERSION_CODES.KITKAT)  
  20.     public static boolean isFloatWindowOpAllowed(Context context) {  
  21.         final int version = Build.VERSION.SDK_INT;  
  22.   
  23.         if(!DeviceUtil.isFlyme4() && !DeviceUtil.isMiui(context)){  
  24.             return true;  
  25.         }  
  26.   
  27.         if (version >= 19) {  
  28.             return checkOp(context, 24);  //自己寫就是24 爲什麼是24?看AppOpsManager //AppOpsManager.OP_SYSTEM_ALERT_WINDOW  
  29.         } else {  
  30.             if(DeviceUtil.isMiui(context)){  
  31.                 if ((context.getApplicationInfo().flags & 1 << 27) == 1 <<27 ) {  
  32.                     return true;  
  33.                 } else {  
  34.                     return false;  
  35.                 }  
  36.             }else{  
  37.                 return true;  
  38.             }  
  39.         }  
  40.   
  41.     }  
  42.   
  43.     @TargetApi(Build.VERSION_CODES.KITKAT)  
  44.     public static boolean checkOp(Context context, int op) {  
  45.         final int version = Build.VERSION.SDK_INT;  
  46.   
  47.         if (version >= 19) {  
  48.             AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);  
  49.             try {  
  50.                 Class managerClass = manager.getClass();  
  51.                 Method method = managerClass.getDeclaredMethod("checkOp", int.class, int.class, String.class);  
  52.                 int isAllowNum = (Integer) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());  
  53.   
  54.                 if (AppOpsManager.MODE_ALLOWED == isAllowNum) {  
  55.                     return true;  
  56.                 } else {  
  57.                     return false;  
  58.                 }  
  59.             } catch (Exception e) {  
  60.                 e.printStackTrace();  
  61.             }  
  62.         }  
  63.         return false;  
  64.     }  
  65.   
  66. }  

發佈了27 篇原創文章 · 獲贊 52 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章