CTS問題分析1

CTS/GTS問題分析1

問題初探

測試命令: run gts -m GtsGmscoreHostTestCases -t com.google.android.gts.devicepolicy.managedprovisioning.DeviceOwnerProvisioningHostsideTest#testRequiredAppsInManagedDevice

報錯堆棧
07-18 16:53:12 I/XtsHostTestBase: Test com.google.android.gts.playstore.ResetPreferredAppsTest#testPersistDefaultBrowser: PASSED
07-18 16:53:19 I/XtsHostTestBase: Test com.google.android.gts.managedprovisioning.AfwRequiredAppsTest#testRequiredApps_DeviceOwner_withGms: FAILURE
07-18 16:53:19 W/XtsHostTestBase: junit.framework.AssertionFailedError: com.google.android.gms is not installed

從這個堆棧很明顯看出在測試device owner相關(guān)的測試時找不到了gmscore apk導(dǎo)致的問題,測了一下,發(fā)現(xiàn)測試這條確實會復(fù)現(xiàn)gmscore被刪除的情況嘲碱,進一步查看log:

07-18 16:53:15.570 14121 14139 D ManagedProvisioning: Deleting package [com.miui.securitycenter] as user 0
07-18 16:53:15.571 14121 14139 D ManagedProvisioning: Deleting package [com.miui.gallery] as user 0

07-18 16:53:15.571 14121 14139 D ManagedProvisioning: Deleting package [com.xiaomi.bttester] as user 0
07-18 16:53:15.571 14121 14139 D ManagedProvisioning: Deleting package [com.xiaomi.market] as user 0
07-18 16:53:15.572 14121 14139 D ManagedProvisioning: Deleting package [com.google.android.gms] as user 0
07-18 16:53:15.572 14121 14139 D ManagedProvisioning: Deleting package [com.android.browser] as user 0
07-18 16:53:15.572 14121 14139 D ManagedProvisioning: Deleting package [com.miui.video] as user 0
07-18 16:53:15.577 14121 14139 D ManagedProvisioning: Deleting package [com.android.mms] as user 0
07-18 16:53:15.578 14121 14139 D ManagedProvisioning: Deleting package [com.android.thememanager] as user 0
07-18 16:53:15.578 14121 14139 D ManagedProvisioning: Deleting package [com.android.camera] as user 0
07-18 16:53:15.579 14121 14139 D ManagedProvisioning: Deleting package [com.miui.bugreport] as user 0
07-18 16:53:15.579 14121 14139 D ManagedProvisioning: Deleting package [com.android.calendar] as user 0
07-18 16:53:15.581 14121 14139 D ManagedProvisioning: Deleting package [com.android.soundrecorder] as user 0

可見測試過程中的確將com.google.android.gms刪除了算行,導(dǎo)致case fail涵防;同時case中斷列另,導(dǎo)致最后測完之后刪除了很多pkg

這條case與device owner有關(guān)覆山,創(chuàng)建device_owner的過程基本與創(chuàng)建managed profile一致京革,需要用到ManagedProvisioning.apk這個apk,流程大致如下:

  1. 啟動ProvisioningActivity辫樱,調(diào)用maybeStartProvisioning
84 @Override
 
85 protected void onCreate(Bundle savedInstanceState) {
86  super.onCreate(savedInstanceState);
87  mParams = getIntent().getParcelableExtra(ProvisioningParams.EXTRA_PROVISIONING_PARAMS);
88  initializeUi(mParams);
89
90  if (savedInstanceState == null
91      || !savedInstanceState.getBoolean(KEY_PROVISIONING_STARTED)) {
92      getProvisioningManager().maybeStartProvisioning(mParams);
93   }
94 }
  1. maybeStartProvisioning中創(chuàng)建ProvisioningController
121 private void startNewProvisioningLocked(final ProvisioningParams params) {
122     ProvisionLogger.logd("Initializing provisioning process");
        ...
132     mController = mFactory.createProvisioningController(mContext, params, this);
133     mController.start(mHandlerThread.getLooper());
134 }
 
35 /**
36 * This method constructs the controller used for the given type of provisioning.
37 */
38 @VisibleForTesting
39 public AbstractProvisioningController createProvisioningController(
40 Context context,
41 ProvisioningParams params,
42 ProvisioningControllerCallback callback) {
43  if (mUtils.isDeviceOwnerAction(params.provisioningAction)) {
44      return new DeviceOwnerProvisioningController(
45          context,
46          params,
47          UserHandle.myUserId(),
48          callback);
49  } else {
        ...
55  }
56 }
  1. 其中初始化需要的task,我們需要看的是DeleteNonRequiredAppsTask
60 protected void setUpTasks() {
61  addTasks(new DeviceOwnerInitializeProvisioningTask(mContext, mParams, this));
    ...
74  addTasks(
75      new DeleteNonRequiredAppsTask(true /* new profile */, mContext, mParams, this),
        ...
78  );
79
 
83 }
  1. DeleteNonRequiredAppsTask刪除不必要的apk
73 @Override
74 public void run(int userId) {
75  Set<String> packagesToDelete = mLogic.getSystemAppsToRemove(userId);
    ...
86  PackageDeleteObserver packageDeleteObserver =
87  new PackageDeleteObserver(packagesToDelete.size());
88  for (String packageName : packagesToDelete) {
89      ProvisionLogger.logd("Deleting package [" + packageName + "] as user " + userId);
90      mPm.deletePackageAsUser(packageName, packageDeleteObserver,
91      PackageManager.DELETE_SYSTEM_APP, userId);
92  }
93 }
  1. 通過NonRequiredAppsLogic峭拘,獲取packagesToDelete
96 public Set<String> getSystemAppsToRemove(int userId) {
        ...
110     Set<String> packagesToDelete = mProvider.getNonRequiredApps(userId);
111
112     // Retain only new system apps
113     packagesToDelete.retainAll(newSystemApps);
114
115     return packagesToDelete;
116 }

6.通過OverlayPackagesProvider提供需要刪除的app

139 public Set<String> getNonRequiredApps(int userId) {
140     if (mLeaveAllSystemAppsEnabled) {
141         return Collections.emptySet();
142     }
143
144     Set<String> nonRequiredApps = getCurrentAppsWithLauncher(userId);
145     // Newly installed system apps are uninstalled when they are not required and are either
146     // disallowed or have a launcher icon.
147     nonRequiredApps.removeAll(getRequiredApps());
148     // Don't delete the system input method packages in case of Device owner provisioning.
149     if (mProvisioningType == DEVICE_OWNER || mProvisioningType == MANAGED_USER) {
150         nonRequiredApps.removeAll(getSystemInputMethods());
151     }
152     nonRequiredApps.addAll(getDisallowedApps());
153     return nonRequiredApps;
154 }

根據(jù)以往經(jīng)驗,gmscore這個package name是需要保留下來的狮暑,即在RequiredApps里面

  1. Required App獲取
73 OverlayPackagesProvider(
74  Context context,
75  ProvisioningParams params,
76  IInputMethodManager iInputMethodManager) {
        ...
92      switch (params.provisioningAction) {
        ...
107         case ACTION_PROVISION_MANAGED_DEVICE:
108         case ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE:
109             mProvisioningType = DEVICE_OWNER;
110             requiredAppsListArray = R.array.required_apps_managed_device;
111             disallowedAppsListArray = R.array.disallowed_apps_managed_device;
112             vendorRequiredAppsListArray = R.array.vendor_required_apps_managed_device;
113             vendorDisallowedAppsListArray = R.array.vendor_disallowed_apps_managed_device;
114             break;
        ...
        }
120     Resources resources = context.getResources();
121     mRequiredAppsList = Arrays.asList(resources.getStringArray(requiredAppsListArray));
122     mDisallowedAppsList = Arrays.asList(resources.getStringArray(disallowedAppsListArray));
123     mVendorRequiredAppsList = Arrays.asList(
124     resources.getStringArray(vendorRequiredAppsListArray));
125     mVendorDisallowedAppsList = Arrays.asList(
126     resources.getStringArray(vendorDisallowedAppsListArray));
127 }

可見required apps其實是根據(jù)ManagedProvisioning.apk里面相關(guān)資源
required_apps_managed_device.xml
vendor_required_apps_managed_device.xml

通過以上的邏輯我們就有了第一個懷疑鸡挠,是不是因為apk里的相關(guān)資源沒有配置正確導(dǎo)致的問題。因此下面向著這個猜想的方向進行驗證

驗證猜想

首先想到的是反編譯查看ManagedProvisioning.apk的values下的arrays資源搬男,然而奇怪的是反編譯的res文件夾下沒有values文件夾拣展。因此我們只能刷機調(diào)試,D5X結(jié)果:

調(diào)試結(jié)果.png

發(fā)現(xiàn)果然沒有g(shù)ms相關(guān)包名缔逛,為了驗證猜想备埃,用E7S的global版本進行驗證姓惑,結(jié)果如下:

對比機驗證結(jié)果.png

通過調(diào)試結(jié)果驗證了猜想,發(fā)現(xiàn)是vendor_required_apps_managed_device.xml的值不正確導(dǎo)致的問題按脚;同時于毙,通過觀察調(diào)試值,很容易想到是vendor_required_apps_managed_device.xml這個屬性的值overlay的不正確導(dǎo)致的問題辅搬;

即原本應(yīng)該overlay的屬性是vendor/google/products/gms_overlay/packages/apps/ManagedProvisioning/res/values/唯沮,但實際上被overlay的屬性是miui/config-overlay/v6/global/packages/apps/ManagedProvisioning/res/values/;

那么進一步想到兩種修改方案:

1.在實際被overlay的屬性中添加gms相關(guān)包名

  1. overlay正確結(jié)果

問題解決

以上兩種方案堪遂,對方案1來說介蛉,雖然簡單,擔不是非常好溶褪,因為這是用來overlay miui的相關(guān)屬性的币旧;但是我們可以先修改這個文件進行測試,添加gms相關(guān)包名后竿滨,發(fā)現(xiàn)case順利通過佳恬,且被刪除的app順利還原;那么放心了于游,只要overlay正確的屬性值就可以順利解決這個問題;

那么開始調(diào)研方案2:
通過在mk文件里加log發(fā)現(xiàn):
首先調(diào)用到了miui/build/common_var.mk中的

MIUI_CONFIG_OVERLAY_ROOT := miui/config-overlay/$(MIUI_VERSION_DIR)

MIUI_CONFIG_OVERLAY_ROOT的值就是miui/config-overlay/v6

然后在miui/device/common/common.mk中

MIUI_PRODUCT_PACKAGE_OVERLAYS += $(MIUI_CONFIG_OVERLAY_ROOT)/$(MIUI_COMMON_DIR)

也就是先overlay了miui/config-overlay/v6/common里面的屬性

接下來還會調(diào)用miui/device/common/v6/common.mk中的

ifeq (true,$(MIUI_HAS_GMSCORE))
   $(call inherit-product-if-exists, vendor/google/google_cn/products/common_cn_gms.mk)
endif

PRODUCT_PACKAGE_OVERLAYS := vendor/google/products/gms_overlay
PRODUCT_PACKAGE_OVERLAYS += vendor/google/google_cn/products/gms_overlay

發(fā)現(xiàn)vendor/google/products下面的overlay確實也會被調(diào)用垫言,且在miui/config-overlay/v6/common之后贰剥,那么按照正常的思路,后面overlay應(yīng)該會覆蓋前面overlay的屬性,那么應(yīng)該不會出錯才對

接下來就繼續(xù)猜測為什么沒有overlay成功筷频?

我們會發(fā)現(xiàn)蚌成,在前面overlay時用的是MIUI_PRODUCT_PACKAGE_OVERLAYS,而后面的是PRODUCT_PACKAGE_OVERLAYS凛捏,會不會是這里產(chǎn)生的影響?

查看代碼担忧,果然:

PRODUCT_PACKAGE_OVERLAYS := $(MIUI_PRODUCT_PACKAGE_OVERLAYS) $(PRODUCT_PACKAGE_OVERLAYS)

如果有MIUI_PRODUCT_PACKAGE_OVERLAYS的overlay,那么這個優(yōu)先級更高
在common_cn_gms.mk中
PRODUCT_PACKAGE_OVERLAYS := vendor/google/products/gms_overlay
MIUI_PRODUCT_PACKAGE_OVERLAYS += vendor/google/google_cn/products/gms_overlay

并在vendor/google/google_cn下面增加相應(yīng)需要預(yù)置的overlay屬性坯癣;但是令人驚訝的是測試結(jié)果還是fail瓶盛,說明overlay還是沒有成功。

加log打印MIUI_PRODUCT_PACKAGE_OVERLAYS示罗,發(fā)現(xiàn)兩個路徑都在變量 MIUI_PRODUCT_PACKAGE_OVERLAYS中惩猫,且vendor/google/google_cn在后面,因此只能懷疑一開始的默認假設(shè): 后面overlay應(yīng)該會覆蓋前面overlay的

則修改miui/device/common /common.mk

ifeq (true,$(PRODUCT_BUILD_INTERNATIONAL))
    MIUI_PRODUCT_PACKAGE_OVERLAYS += $(MIUI_CONFIG_OVERLAY_ROOT)/$(MIUI_GLOBAL_DIR)
else
    ifeq (true,$(MIUI_HAS_GMSCORE))
        MIUI_PRODUCT_PACKAGE_OVERLAYS += vendor/google/google_cn/products/gms_overlay
    endif
endif
MIUI_PRODUCT_PACKAGE_OVERLAYS += $(MIUI_CONFIG_OVERLAY_ROOT)/$(MIUI_COMMON_DIR)

case pass蚜点,因此對于PRODUCT_PACKAGE_OVERLAYS轧房,寫在前面的目錄優(yōu)先級高于寫在后面目錄的優(yōu)先級

到這里,問題已經(jīng)得到了解決绍绘,這個問題其實不難奶镶,但是因為理清了我的一個思維誤區(qū)迟赃,因此覺得有必要記錄下來。

問題拓展

為什么資源的overlay是從前往后的厂镇,感覺這個不符合邏輯啊纤壁,稍稍調(diào)研了下。

不管如何剪撬,最后要將資源打進apk摄乒,最終會使用aapt,那么全局搜了一下残黑,最終發(fā)現(xiàn)了相關(guān)的mk馍佑,build/core/definitions.mk

2027$(hide) $(AAPT) package $(PRIVATE_AAPT_FLAGS) -m \
2028 $(eval # PRIVATE_PRODUCT_AAPT_CONFIG is intentionally missing-- see comment.) \
2029 $(addprefix -J , $(PRIVATE_SOURCE_INTERMEDIATES_DIR)) \
2030 $(addprefix -M , $(PRIVATE_ANDROID_MANIFEST)) \
2031 $(addprefix -P , $(PRIVATE_RESOURCE_PUBLICS_OUTPUT)) \
2032 $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
2033 $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
2034 $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \
2035 $(addprefix -G , $(PRIVATE_PROGUARD_OPTIONS_FILE)) \
2036 $(addprefix --min-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
2037 $(addprefix --target-sdk-version , $(PRIVATE_DEFAULT_APP_TARGET_SDK)) \
2038 $(if $(filter --version-code,$(PRIVATE_AAPT_FLAGS)),,--version-code $(PLATFORM_SDK_VERSION)) \
2039 $(if $(filter --version-name,$(PRIVATE_AAPT_FLAGS)),,--version-name $(APPS_DEFAULT_VERSION_NAME)) \
2040 $(addprefix --rename-manifest-package , $(PRIVATE_MANIFEST_PACKAGE_NAME)) \
2041 $(addprefix --rename-instrumentation-target-package , $(PRIVATE_MANIFEST_INSTRUMENTATION_FOR)) \
2042 --skip-symbols-without-default-localization
2043endef

注意這里

$(hide) $(AAPT) package $(PRIVATE_AAPT_FLAGS) -m \
$(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \ 

可見,在dir目錄下尋找資源其實用的是aapt -S
那么后面的PRIVATE_RESOURCE_DIR是什么呢梨水?

$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)

在build/core/package_internal.mk中

94 $(wildcard $(foreach dir, $(PRODUCT_PACKAGE_OVERLAYS), \
95 $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))) \
96 $(wildcard $(foreach dir, $(DEVICE_PACKAGE_OVERLAYS), \
97 $(addprefix $(dir)/, $(LOCAL_RESOURCE_DIR)))))
 
124ifndef enforce_rro_enabled
125 LOCAL_RESOURCE_DIR := $(package_resource_overlays) $(LOCAL_RESOURCE_DIR)
126endif

可見拭荤,overlay就是將PRODUCT_PACKAGE_OVERLAYS里面的dir取出,添加到LOCAL_RESOURCE_DIR的前面

最后在packages/apps/ManagedProvisioning目錄下執(zhí)行mma疫诽,打出$(LOCAL_RESOURCE_DIR)

vendor/google/google_cn/products/gms_overlay/packages/apps/ManagedProvisioning/res miui/config-overlay/v6/common/packages/apps/ManagedProvisioning/res vendor/google/google_cn/products/gms_overlay/packages/apps/ManagedProvisioning/res vendor/google/products/gms_overlay/packages/apps/ManagedProvisioning/res vendor/google/google_cn/products/gms_overlay/packages/apps/ManagedProvisioning/res packages/apps/ManagedProvisioning/res frameworks/opt/setupwizard/library//main/res frameworks/opt/setupwizard/library//platform/res

果然vendor/google/google_cn/products/gms_overlay/packages/apps/ManagedProvisioning/res在miui/config-overlay/v6/common/packages/apps/ManagedProvisioning/res 的前面了

再來查看aapt -S的意思

[-S resource-sources [-S resource-sources ...]] \\\n"

130 " -S directory in which to find resources. Multiple directories will be scanned\n"
131 " and the first match found (left to right) will take precedence.\n"

467 case 'S':
468     argc--;
469     argv++;
470     if (!argc) {
471         fprintf(stderr, "ERROR: No argument supplied for '-S' option\n");
472         wantUsage = true;
473         goto bail;
474     }
475     convertPath(argv[0]);
476     bundle.addResourceSourceDir(argv[0]);
477     break;

aapt -S 取第一個路徑進行overlay

問題總結(jié)

這個問題其實沒什么難度舅世,但是從中理清了overlay順序的理解誤區(qū),因此記錄一下奇徒;寫的比較粗糙雏亚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市摩钙,隨后出現(xiàn)的幾起案子罢低,更是在濱河造成了極大的恐慌,老刑警劉巖胖笛,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件网持,死亡現(xiàn)場離奇詭異,居然都是意外死亡长踊,警方通過查閱死者的電腦和手機功舀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來身弊,“玉大人辟汰,你說我怎么就攤上這事∮铀ⅲ” “怎么了莉擒?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長瘫絮。 經(jīng)常有香客問我涨冀,道長,這世上最難降的妖魔是什么麦萤? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任鹿鳖,我火速辦了婚禮扁眯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翅帜。我一直安慰自己姻檀,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布涝滴。 她就那樣靜靜地躺著绣版,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歼疮。 梳的紋絲不亂的頭發(fā)上杂抽,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音韩脏,去河邊找鬼缩麸。 笑死,一個胖子當著我的面吹牛赡矢,可吹牛的內(nèi)容都是我干的杭朱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼吹散,長吁一口氣:“原來是場噩夢啊……” “哼弧械!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起空民,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤梦谜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后袭景,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡闭树,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年耸棒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片报辱。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡与殃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碍现,到底是詐尸還是另有隱情幅疼,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布昼接,位于F島的核電站爽篷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏慢睡。R本人自食惡果不足惜逐工,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一铡溪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸康铭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捆探。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滑肉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工对途, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赦邻,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓实檀,卻偏偏與公主長得像惶洲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膳犹,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345