天天看點

Lollipop DevicePolicyManager學習(上)

Android 5.0(lollipop)釋出之後,看特性文檔增加了不少有趣的東西。

Lollipop DevicePolicyManager學習(上)

最近花了一些時間,研究了下其中Managed Profile的概念,簡稱MP,記錄下來作為一些經驗,有需要的同學請參考。

簡介

Managed Profile,簡稱被管理者賬戶。這個概念并不是什麼新東西,因為早在4.2版本中,Android就引入了多使用者機制來解決平闆使用上的問題。而如今5.0新加入的這個被管理者賬戶功能,可以了解成為是為了解決使用者本人對于應用進行分類的需求問題而做的細化吧。

存在于被管理者賬戶中的應用受制于主賬戶,也就是仍然處于機主本人的控制之下。但這些應用的存儲空間,以及應用的userID和PID都不同于主賬戶的同名應用。

這些在被管理者賬戶中的應用可以由機主進行各方面的限制,比如說控制這些應用不能通路攝像頭——所有涉及到拍照部分的功能都開啟不了,再比如說控制某些特定應用的功能——比如說讓chrome的曆史記錄功能禁止使用。而所有的這些應用都與主賬戶中的應用隔離,這就意味着原本可能會被無故喚起的某些應用放到這裡之後,它也再也不會被另一些流氓應用給背景喚醒了。

前提條件

首先,你需要一台安裝Android5.0及以上版本的手機,親兒子系列最好,因為不知道第三方ROM是否會将“加密”功能給閹割了。

開啟手機加密的方法為:

設定——安全——加密手機

一般來說,手機出廠設定是不預設加密的,需要使用者自己啟動才行。當然,也可以通過代碼來啟動該功能,具體如下:

private voidregisterPovisionManagerProfile() {
        if (null== this) {
           return;
        }
        Intentintent = new Intent(ACTION_PROVISION_MANAGED_PROFILE);
       intent.putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME,
               this.getApplicationContext().getPackageName());
        if(intent.resolveActivity(this.getPackageManager()) != null) {
           startActivityForResult(intent, REQUEST_PROVISION_MANAGED_PROFILE);
           this.finish();
        } else {
           Toast.makeText(this, "Device provisioning is not enabled.Stopping.",
                   Toast.LENGTH_SHORT).show();
        }
}
 
 
    @Override
    public voidonActivityResult(int requestCode, int resultCode, Intent data) {
        if(requestCode == REQUEST_PROVISION_MANAGED_PROFILE) {
            if(resultCode == Activity.RESULT_OK) {
               Toast.makeText(getApplicationContext(), "Provisioning done.",Toast.LENGTH_SHORT).show();
            }else {
               Toast.makeText(getApplicationContext(), "Provisioningfailed.", Toast.LENGTH_SHORT).show();
            }
           return;
        }
       super.onActivityResult(requestCode, resultCode, data);
    }
           

之後根據引導視窗則可以完成加密手機并設定Managed Profile的流程。

具體子產品設計相關

對手機加密完成,并生成Managed Profile賬戶之後,我考慮了以下幾個問題。

1.      啟動MP賬戶的流程

在google給出的官方樣例中,啟動一個ManagedProfile已經有了一套成熟的方案。

具體如下:

a)        判斷目前應用是否已經注冊為目前賬戶的賬戶所有者(profile owner)——可以了解是擁有某些進階權限,類似admin使用者。

b)        如果不是,則參考加密的流程,發一個系統的intent啟動加密流程。

c)        如果是,那麼恭喜你,你已經處于一個MP中,并且擁有這個MP下的類管理者權限了。

參考google的官方樣例BasicManagedProfile即可。

為了友善後續描述,目前應用我簡稱為AdminApp好了。

2.      如何添加現有應用至MP賬戶中。

一般來說,查詢目前系統中安裝的應用狀态Android已經有了非常友善的方式,通過PackageManager可以查到系統中安裝的各個包的資訊總合,也可以指定特定的包名來查詢對應資訊。

但是,這個在MP賬戶中是做不到的。

比如常見的getInstalledPackages(intflag)方法,雖然平時調用時僅使用參數flags。但從源碼來看,真實的被調用者其實是被隐藏的方法getInstalledPackages(int flags,int userId),暴露給我們的方法中,userId已經固定為目前的使用者ID。

再看一下PackageManager服務程序就能知道,真正在查詢安裝包資訊時,該方法需要将userID作為校驗條件之一。通常一個Profile下對應的所有應用都有一個相同的userID,是以跨了Profile後就無法查詢主賬戶下的應用資訊了。

是以在預設的MP賬戶中操作getInstalledPackages(),如果指明傳回非系統應用,則隻會傳回目前應用本身,其他的三方應用是無法找到的。同樣,查找系統應用也隻能查找到在MP賬戶中注冊的系統應用,沒有注冊的同樣也找不到。

是以,如果要添加相關應用至MP賬戶中,無法通過輪詢目前被管理者賬戶下所有的應用名稱來一一添加。目前可行的有兩種方法,一種是用包名字串來激活,另一種是從主賬戶AP來擷取包名激活。

其中,谷歌的官方demo BasicManagedProfile使用的第一種方法,這裡先進行介紹這種方法。如何從主賬戶來擷取留在後面介紹。

還是以Chrome應用為例。

Chrome的包名是:com.android.chrome

通過isApplicationEnabled方法可以判斷目前這個應用并沒有在MP賬戶中。具體的原理就是剛才所說的userID隔離後的查詢的結果。

/**
     * Checks if the application is availablein this profile.
     *
     * @param packageName The package name
     * @return True if the application isavailable in this profile.
     */
private boolean isApplicationEnabled(String packageName) {
…
}
           

         Android對已知包名的系統應用,提供了将其重新安裝到被管理者賬戶中的方法供AdminApp來調用。即public void enableSystemApp (ComponentName admin, StringpackageName)。

         具體的使用流程可以參考demo中的代碼段:

/**
    * Enables or disables the specified app in this profile.
    *
    * @param packageName The package name of the target app.
    * @param enabled     Pass true toenable the app.
    */
private voidsetAppEnabled(String packageName, boolean enabled) {
}
           

需要注明的一點是,這個方法隻針對擁有INSTALL_PACKAGES權限的系統應用有效,如果你傳入的第三方應用包名,那麼肯定會抛出IllegalArgumentException:Only system apps canbe enabled this way異常。

即使通過反射直接調用PackageManager服務的installExistingPackageAsUser(packageName,userID)方法,也會因為權限的問題而失敗。

是以,手動添加第三方應用到MP中目前我是沒有找到更好的方法,隻能在建立MP之後重新安裝指定的第三方應用,此時,MP中會同樣安裝一份拷貝版本。

通過AdminApp調用enableSystemApp使能的系統應用會出現在被管理者賬戶中,作為Launch的圖示顯示出來。:

Lollipop DevicePolicyManager學習(上)

同樣,如果不希望該應用顯示在MP中,可以用AdminApp調用publicboolean setApplicationHidden (ComponentName admin, String packageName, booleanhidden)來隐藏。

簡而言之,通過上述的操作,可以将一個系統應用重新安裝到被管理者賬戶中。之後,你可以對這個被管理者賬戶中的應用進行限制操作了。