Android實現(xiàn)靜默安裝和智能安裝

靜默安裝获讳,就是指在程序安裝時,用戶并不會感知到安裝的過程活喊,自己就安裝完成了丐膝。一些系統(tǒng)自帶應用市場會具有靜默安裝的功能,比如小米的應用市場钾菊。在一些非系統(tǒng)自帶的應用市場帅矗,要想完成靜默安裝,就必須具有root權限煞烫』氪耍可見權限的重要性,在系統(tǒng)的支持下红竭,你可以做到很多別人做不到的事情尤勋。當然喘落,像360手機衛(wèi)士茵宪,應用寶,豌豆莢之類的非系統(tǒng)支持的應用市場瘦棋,大多使用了智能安裝稀火,仍然會彈出系統(tǒng)安裝彈窗,但是會迅速自動點擊安裝按鈕赌朋。對大多數(shù)用戶來說凰狞,還是未root用戶比較多,因為這種方式也是很常見的沛慢。

一.靜默安裝

靜默安卓有兩個前提條件赡若,一個是手機必須具有root權限,一個是系統(tǒng)是4.2及以上团甲∮舛看一下靜默安裝的代碼:

 /**
 * 靜默安裝
 * @param apkPath apk文件路徑
 */
public static boolean install(String apkPath) {
    boolean result = false;
    DataOutputStream dataOutputStream = null;
    BufferedReader errorStream = null;
    try {
        Process process = Runtime.getRuntime().exec("su");//申請root權限
        dataOutputStream = new DataOutputStream(process.getOutputStream());

        String command = "pm install -r " + apkPath + "\n";//拼接 pm install 命令,執(zhí)行。-r表示若存在則覆蓋安裝
        dataOutputStream.write(command.getBytes(Charset.forName("utf-8")));
        dataOutputStream.flush();
        dataOutputStream.writeBytes("exit\n");
        dataOutputStream.flush();
        process.waitFor();//安裝過程是同步的身腻,安裝完成后再讀取結果
        errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String message = "";
        String line;
        while ((line = errorStream.readLine()) != null) {
            message += line;
        }
        Log.e("silentInstall", message);
        if (!message.contains("Failure")) {
            result = true;
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (dataOutputStream != null) {
                dataOutputStream.close();
            }
            if (errorStream != null) {
                errorStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    return result;
}

代碼不多产还,核心就是調用了這條命令 pm install -r <apkPath>,顯然和我們在adb中安裝apk一樣。-r代表若目標apk存在則覆蓋安裝嘀趟。首先通過Runtime.getRuntime().exec("su")申請root權限脐区,否則是沒有辦法成功執(zhí)行pm命令的。執(zhí)行完成后通過讀取安裝結果她按,判斷是否安裝成功牛隅。我們也可以先判斷一下當前是否具有root權限,再去決定是否靜默安裝酌泰。

/**
 * 判斷手機是否擁有Root權限倔叼。
 * @return 有root權限返回true,否則返回false宫莱。
 */
public static boolean isRoot() {
    boolean bool = false;
    try {
        bool = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return bool;
}

二.智能安裝

智能安裝利用輔助功能AccessibilityService來實現(xiàn)對安裝界面的自動點擊丈攒。什么是AccessibilityService呢?Accessibility services should only be used to assist users with disabilities in using Android devices and apps. They run in the background and receive callbacks by the system when AccessibilityEvents are fired.它是被設計出來幫助一些無法正常使用安卓設備和app的殘疾人的授霸,運行在后臺巡验,當系統(tǒng)發(fā)生一些AccessibilityEvent時會產生回調。通過這些回調碘耳,我們可以通過代碼做一些事情显设。這里我們要做的就是,當系統(tǒng)安裝彈窗出現(xiàn)時辛辨,通過AccessibilityService自動點擊安裝或者確定按鈕捕捂。

首先是做一個配置,在res/xml目錄下新建accessibility_service_config.xml文件斗搞,內容如下:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:packageNames="com.android.packageinstaller"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canRetrieveWindowContent="true"/>
  • packageNames,Comma separated package names from which this serivce would like to receive events (leave out for all packages).指定我們的Service監(jiān)聽哪個包名下的事件指攒。這里的com.android.packageinstaller就是指安卓的安裝界面。
  • description,當在輔助功能界面點擊你自己的app后僻焚,會顯示這些文字允悦。你可以描述你開啟輔助功能的目的,為自己狡辯一下虑啤。
  • accessibilityEventTypes,The event types this serivce would like to receive as specified in AccessibilityEvent.我們可以在監(jiān)聽窗口中模擬哪些事件隙弛。typeAllMask代表所有。
  • accessibilityFlags,Additional flags as specified in AccessibilityServiceInfo.一些附加參數(shù)狞山,默認即可全闷。
  • accessibilityFeedbackType,無障礙服務的反饋方式,比如針對殘疾人可以語音反饋萍启,這里并不需要总珠。
  • canRetrieveWindowContent,Attribute whether the accessibility service wants to be able to retrieve the active window content. 我們是否可以檢索窗口中的內容,當然應該是可以的。

然后我們需要繼承AccessibilityService,重寫相關方法實現(xiàn)智能安裝的具體邏輯姚淆。如下所示:

/**
 * Created by Lu
 * on 2017/1/17 17:54.
 */

public class MyAccessibilityService extends AccessibilityService {

Map<Integer, Boolean> handleMap = new HashMap<>();

/**
 * 當窗口有活動時孕蝉,會回調此方法
 * @param event
 */
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    AccessibilityNodeInfo nodeInfo = event.getSource();
    if (nodeInfo != null) {
        int eventType = event.getEventType();
        if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {
            if (handleMap.get(event.getWindowId()) == null) {
                boolean handled = iterateNodesAndHandle(nodeInfo);
                if (handled) {
                    handleMap.put(event.getWindowId(), true);
                }
            }
        }
    }
}

/**
 * 遞歸處理節(jié)點信息
 * 節(jié)點名稱為Button,內容為 安裝腌逢,確定降淮,完成的,模擬點擊
 * 節(jié)點名稱是ScrollView搏讶,模擬滑動到底部
 */
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) {
    if (nodeInfo != null) {
        int childCount = nodeInfo.getChildCount();
        if ("android.widget.Button".equals(nodeInfo.getClassName())) {
            String nodeContent = nodeInfo.getText().toString();
            if ("安裝".equals(nodeContent) || "確定".equals(nodeContent) || "完成".equals(nodeContent)) {
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                return true;
            }
        } else if ("android.widget.ScrollView".equals(nodeInfo.getClassName())) {
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
        }
        for (int i = 0; i < childCount; i++) {
            AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
            if (iterateNodesAndHandle(childNodeInfo)) {
                return true;
            }
        }
    }
    return false;
}

@Override
public void onInterrupt() {

}
}

每次當有新的AccessibilityEvent時佳鳖,就會回調onAccessibilityEvent方法。這里我們處理了兩種事件類型媒惕,AccessibilityEvent.TYPE_WINDOW_STATE_CHANGEDAccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED系吩,分別代表窗體狀態(tài)的變化和窗體內容的變化,符合條件的節(jié)點再對節(jié)點內容進行分析:

  • 當節(jié)點名稱是Button時妒蔚,且節(jié)點內容是安裝 || 確定 || 完成時穿挨,模擬點擊事件AccessibilityNodeInfo.ACTION_CLICK
  • 當節(jié)點名稱是ScrollView時,這時候是在顯示權限列表肴盏,一些系統(tǒng)會要求顯示完全部權限科盛,這時候模擬上滑。

既然是一個Service菜皂,就要去注冊它贞绵。這里大多是固定寫法。

<service android:name=".MyAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>

        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibility_service_config"/>
    </service>

在使用時恍飘,先提醒用戶開啟相應輔助功能榨崩,如下代碼,跳轉到輔助功能設置界面:

 Intent intent=new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
 startActivity(intent);

然后安裝apk章母,

Uri uri=Uri.fromFile(new File(path));
Intent intent=new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri,"application/vnd.android.package-archive");
startActivity(intent);

前提是用戶確實開啟了輔助功能母蛛,這時候才會自動智能安裝。

不管是哪種方式胳施,缺陷都是很大的溯祸。靜默安裝需要足夠的權限肢专,智能安裝需要用戶去開啟輔助功能舞肆,很多用戶應該都不會去操作的〔┱龋或許也正想看看你申請了哪些該死的權限椿胯。面對安卓雜亂的生態(tài)環(huán)境,希望在某些小小的方面可以達成一致性剃根,就像最近Google提出要強制統(tǒng)一通知中心哩盲。如果安卓能有一個統(tǒng)一的通知機制,當然是指在國內。就會少了多少服務相互喚醒廉油,就不會有那么多廠商去標榜自己的通知到達率惠险。

源碼下載

Android靜默安裝實現(xiàn)方案,仿360手機助手秒裝和智能安裝功能 ——————郭霖
https://developer.android.com/guide/topics/ui/accessibility/services.html

有任何疑問抒线,歡迎加群討論:261386924

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末班巩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嘶炭,更是在濱河造成了極大的恐慌抱慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眨猎,死亡現(xiàn)場離奇詭異抑进,居然都是意外死亡,警方通過查閱死者的電腦和手機睡陪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門弦悉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诅需,你說我怎么就攤上這事螺捐。” “怎么了逮矛?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵鸡号,是天一觀的道長。 經常有香客問我须鼎,道長鲸伴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任晋控,我火速辦了婚禮汞窗,結果婚禮上,老公的妹妹穿的比我還像新娘赡译。我一直安慰自己仲吏,他們只是感情好,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布蝌焚。 她就那樣靜靜地躺著裹唆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪只洒。 梳的紋絲不亂的頭發(fā)上许帐,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天,我揣著相機與錄音毕谴,去河邊找鬼成畦。 笑死距芬,一個胖子當著我的面吹牛,可吹牛的內容都是我干的循帐。 我是一名探鬼主播框仔,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拄养!你這毒婦竟也來了存和?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤衷旅,失蹤者是張志新(化名)和其女友劉穎捐腿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柿顶,經...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡茄袖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了嘁锯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宪祥。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖家乘,靈堂內的尸體忽然破棺而出蝗羊,到底是詐尸還是另有隱情,我是刑警寧澤仁锯,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布耀找,位于F島的核電站,受9級特大地震影響业崖,放射性物質發(fā)生泄漏野芒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一双炕、第九天 我趴在偏房一處隱蔽的房頂上張望狞悲。 院中可真熱鬧,春花似錦妇斤、人聲如沸摇锋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荸恕。三九已至,卻和暖如春顷编,著一層夾襖步出監(jiān)牢的瞬間戚炫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工媳纬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留双肤,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓钮惠,卻偏偏與公主長得像茅糜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子素挽,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內容