Android——鎖定launch - 原生Browser啟動 -引導provision

來源:互聯網
上載者:User

標籤:category.home   launch   startactivity   provision   settings.global.devi   

     前段時間做了一個功能,就是鎖定主launch,機器上只能跑我們定義的launch,當時沒注意影響,

最近發現就是因為在AMS中加了這個鎖定過濾條件導致原生Browser無法啟動了,

把我鬱悶的,當時怎麼想都覺得奇怪,這完全不相關的兩件事怎麼會影響到~ 這裡記錄一下



                                                                              撰寫不易,轉載請註明出處:http://blog.csdn.net/jscese/article/details/41015941



鎖定主launch

    啟動android系統launch的過程原理可參考Android——啟動過程詳解 中的HOME啟動,

 這個網上的方法比較多,最常見的就是修改原生的 CATEGORY_HOME 變數或者添加一個新的變數來做篩選條件,需要修改源碼中出現CATEGORY_HOME的地方.

比如我在/frameworks/base/core/java/android/content/Intent.java 中把CATEGORY_HOME 改為:

public static final String CATEGORY_HOME = "android.intent.category.JSCESE_HOME";

這樣的話在源碼中其它地方使用的 CATEGORY_HOME 變數都可以不動,整體編譯需要使用 make update-api更新api.

然後只需要把我們想要當作launch的apk的AndroidManifest.xml檔案中:

<category android:name="android.intent.category.HOME" />//改為<category android:name="android.intent.category.JSCESE_HOME" />

這樣一來只有定義了andorid.intent.category.JSCESE_HOME 這個category的launch 才能被系統當作HOME launch啟動起來!

貌似一看沒有任何問題~





原生Browser啟動

    像上面介紹的那樣可以正常鎖定住我們指定的launch,但是問題來了~,當啟動Browser的時候直接就退出來了,啟動其它的apk,或者使用其它的瀏覽器都能正常運行,

我一查發現退出原因是在BrowserActivity.java中的:

private boolean shouldIgnoreIntents() {...  ignore |= mKeyguardManager.inKeyguardRestrictedInputMode();   //這裡為 true !代表鍵盤鎖定 導致程式finishreturn ignore;}

最後一路跟蹤調試,WindowManagerService——>PhoneWindowManager——>KeyguardViewMediator中的:

    /**     * Given the state of the keyguard, is the input restricted?     * Input is restricted when the keyguard is showing, or when the keyguard     * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.     */    public boolean isInputRestricted() {    Log.d(TAG,"jscese display mShowing =="+mShowing+"  "+mNeedToReshowWhenReenabled+"  "+!mUpdateMonitor.isDeviceProvisioned());        return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();    }

我發現 !mUpdateMonitor.isDeviceProvisioned()==true

追根溯源到KeyguardUpdateMonitor.java中的:

mDeviceProvisioned = Settings.Global.getInt(                mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0;

問題就出在這裡,這裡得到的是 0 ~

我果斷的到setting.db中去查了下global表中的device_provisioned:

select * from global where name='device_provisioned';

果然在db中的值為 0 ! 查詢方法可參考:Android——sqlite3 基本命令操作

源碼全域一搜,發現在/packages/apps/Provision/src/com/android/provision/DefaultActivity.java 中有這麼一行:

 Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);

馬蛋,我瞬間明白了前因後果,我看這個DefaultActivity.java 也眼熟... 原來是漏掉了這個真正的 第一 個 apk





引導provision

 這個引導apk一般被很多人忽視,這次我也忽視掉了,最開始接觸android的時候還知道這東西,久沒接觸給忘掉了~

這個apk是作為android第一引導apk的,AndroidManifest.xml中和一般的launch一樣定義的:

            <intent-filter android:priority="1">                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.HOME" />                <category android:name="android.intent.category.DEFAULT" />            </intent-filter>

所以其實在 AMS中的 startHomeActivityLocked 啟動HOME activity的時候,這個 provision的 DefaultActivity也是被查詢出來的,

而且因為優先順序=1 高於 一般的launch,而被直接啟動,不算作多HOME launch.

看下這個 DefaultActivity.java:

/** * Application that sets the provisioned bit, like SetupWizard does. */public class DefaultActivity extends Activity {    @Override    protected void onCreate(Bundle icicle) {        super.onCreate(icicle);        // Add a persistent setting to allow other apps to know the device has been provisioned.        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);   // 這裡設定了一個狀態值 ,也就是上面說到的 device_provisioned,代表升級完成,裝置準備好了~        // remove this activity from the package manager.        PackageManager pm = getPackageManager();        ComponentName name = new ComponentName(this, DefaultActivity.class);        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,   //這個是直接從packagemanager中 把自己剔除,也就是說 這個activity 只啟動這麼一次                PackageManager.DONT_KILL_APP);        // terminate the activity.        finish();    }}

這個引導除了設定準備完成標誌,把自己屏蔽掉之外沒做什麼其它操作,

DEVICE_PROVISIONED:

上面有說道設定進了setting.db的global表裡面,這個標誌很重要,像上面就是因為鍵盤檢測這個標誌還為 0 ,導致鍵盤是鎖定的狀態,無法使用Browser,

另外還有 鎖屏程式不會鎖屏;對HOME key的處理也不同;電話也是打不進來的


另外從PackageManager中剔除的操作儲存在 /data/system目錄下的packages.xml中


另外注釋上有看到 這個activity 是用來做設定嚮導的~所以一些第一次起機需要做的一些操作可以加在這裡讓其啟動:

Intent intent = new Intent();ComponentName componentName = new ComponentName("com.xxx.xxx", "com.xxx.xxx.yourAcitvity");intent.setComponent(componentName);startactivity(intent); 

也有直接在這裡直接查詢HOME的 ,然後跳轉指定的 launch~


像我上面說的那種鎖定launch,就是因為啟動時漏掉了這個引導provision,所以無法啟動Browser,而且還有其它的功能隱患!

解決辦法就是把這個Provision的AndroidManifest.xml 也:

<category android:name="android.intent.category.HOME" />//改為<category android:name="android.intent.category.JSCESE_HOME" />





Intent隱式啟動,Activity啟動選擇框

  當使用intent隱式啟動activity時,都是通過PackageManager 查詢滿足條件的activity,如果有不只一項滿足,那麼就會彈出一個dialog,讓使用者選擇!

上面說到的多個HOME launch狀態下,想要鎖定我自己的launch也是出於這個原因!

大體記錄一下流程:

 一般啟動一個activity時都是context.startActivity(intent)之類的,初步調用到/frameworks/base/core/java/android/app/ContextImpl.java:

    @Override    public void startActivity(Intent intent, Bundle options) {        warnIfCallingFromSystemProcess();        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {            throw new AndroidRuntimeException(                    "Calling startActivity() from outside of an Activity "                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."                    + " Is this really what you want?");        }        mMainThread.getInstrumentation().execStartActivity(            getOuterContext(), mMainThread.getApplicationThread(), null,            (Activity)null, intent, -1, options);    }

調用到同目錄下的Instrumentation.java中的execStartActivity,再調用ActivityManagerNative.getDefault().startActivity(*);

很明顯接下來就是調用到AMS中的startAcitivity

    public final int startActivity(IApplicationThread caller,            Intent intent, String resolvedType, IBinder resultTo,            String resultWho, int requestCode, int startFlags,            String profileFile, ParcelFileDescriptor profileFd, Bundle options) {        return startActivityAsUser(caller, intent, resolvedType, resultTo, resultWho, requestCode,                startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());    }    public final int startActivityAsUser(IApplicationThread caller,            Intent intent, String resolvedType, IBinder resultTo,            String resultWho, int requestCode, int startFlags,            String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) {        enforceNotIsolatedCaller("startActivity");        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,                false, true, "startActivity", null);        return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,                null, null, options, userId);    }

調用到/frameworks/base/services/java/com/android/server/am/ActivityStack.java中:

final int startActivityMayWait(IApplicationThread caller, int callingUid,            Intent intent, String resolvedType, IBinder resultTo,            String resultWho, int requestCode, int startFlags, String profileFile,            ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,            Bundle options, int userId) {...   newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,                                aInfo.packageName);                        newIntent.setFlags(intent.getFlags());                        newIntent.setClassName("android",                                HeavyWeightSwitcherActivity.class.getName());  //可以看到符合上面的一系列判定條件之後,發現如果是多個activity滿足條件,在這裡就先啟動了一個選擇activity                        intent = newIntent;...   int res = startActivityLocked(caller, intent, resolvedType,                    aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,                    startFlags, options, componentSpecified, null);...return res;}

可以看下/frameworks/base/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java:

/** * This activity is displayed when the system attempts to start an Intent for * which there is more than one matching activity, allowing the user to decide * which to go to.  It is not normally used directly by application developers. */public class HeavyWeightSwitcherActivity extends Activity {    /** The PendingIntent of the new activity being launched. */    public static final String KEY_INTENT = "intent";    /** Set if the caller is requesting a result. */    public static final String KEY_HAS_RESULT = "has_result";    /** Package of current heavy-weight app. */    public static final String KEY_CUR_APP = "cur_app";    /** Task that current heavy-weight activity is running in. */    public static final String KEY_CUR_TASK = "cur_task";    /** Package of newly requested heavy-weight app. */    public static final String KEY_NEW_APP = "new_app";    


注釋寫的很明白~





Android——鎖定launch - 原生Browser啟動 -引導provision

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.