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相关的测试时找不到了gmscore apk导致的问题,测了一下,发现测试这条确实会复现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删除了,导致case fail;同时case中断,导致最后测完之后删除了很多pkg

这条case与device owner有关,创建device_owner的过程基本与创建managed profile一致,需要用到ManagedProvisioning.apk这个apk,流程大致如下:

  1. 启动ProvisioningActivity,调用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中创建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 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 getSystemAppsToRemove(int userId) {
        ...
110     Set 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 getNonRequiredApps(int userId) {
140     if (mLeaveAllSystemAppsEnabled) {
141         return Collections.emptySet();
142     }
143
144     Set 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 }

根据以往经验,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其实是根据ManagedProvisioning.apk里面相关资源
required_apps_managed_device.xml
vendor_required_apps_managed_device.xml

通过以上的逻辑我们就有了第一个怀疑,是不是因为apk里的相关资源没有配置正确导致的问题。因此下面向着这个猜想的方向进行验证

验证猜想

首先想到的是反编译查看ManagedProvisioning.apk的values下的arrays资源,然而奇怪的是反编译的res文件夹下没有values文件夹。因此我们只能刷机调试,D5X结果:

调试结果.png

发现果然没有gms相关包名,为了验证猜想,用E7S的global版本进行验证,结果如下:

对比机验证结果.png

通过调试结果验证了猜想,发现是vendor_required_apps_managed_device.xml的值不正确导致的问题;同时,通过观察调试值,很容易想到是vendor_required_apps_managed_device.xml这个属性的值overlay的不正确导致的问题;

即原本应该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相关包名

  1. overlay正确结果

问题解决

以上两种方案,对方案1来说,虽然简单,担不是非常好,因为这是用来overlay miui的相关属性的;但是我们可以先修改这个文件进行测试,添加gms相关包名后,发现case顺利通过,且被删除的app顺利还原;那么放心了,只要overlay正确的属性值就可以顺利解决这个问题;

那么开始调研方案2:
通过在mk文件里加log发现:
首先调用到了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里面的属性

接下来还会调用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

发现vendor/google/products下面的overlay确实也会被调用,且在miui/config-overlay/v6/common之后,那么按照正常的思路,后面overlay应该会覆盖前面overlay的属性,那么应该不会出错才对

接下来就继续猜测为什么没有overlay成功?

我们会发现,在前面overlay时用的是MIUI_PRODUCT_PACKAGE_OVERLAYS,而后面的是PRODUCT_PACKAGE_OVERLAYS,会不会是这里产生的影响?

查看代码,果然:

PRODUCT_PACKAGE_OVERLAYS := $(MIUI_PRODUCT_PACKAGE_OVERLAYS) $(PRODUCT_PACKAGE_OVERLAYS)

如果有MIUI_PRODUCT_PACKAGE_OVERLAYS的overlay,那么这个优先级更高
在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下面增加相应需要预置的overlay属性;但是令人惊讶的是测试结果还是fail,说明overlay还是没有成功。

加log打印MIUI_PRODUCT_PACKAGE_OVERLAYS,发现两个路径都在变量 MIUI_PRODUCT_PACKAGE_OVERLAYS中,且vendor/google/google_cn在后面,因此只能怀疑一开始的默认假设: 后面overlay应该会覆盖前面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,写在前面的目录优先级高于写在后面目录的优先级

到这里,问题已经得到了解决,这个问题其实不难,但是因为理清了我的一个思维误区,因此觉得有必要记录下来。

问题拓展

为什么资源的overlay是从前往后的,感觉这个不符合逻辑啊,稍稍调研了下。

不管如何,最后要将资源打进apk,最终会使用aapt,那么全局搜了一下,最终发现了相关的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目录下执行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

问题总结

这个问题其实没什么难度,但是从中理清了overlay顺序的理解误区,因此记录一下;写的比较粗糙。

你可能感兴趣的:(CTS问题分析1)