//
刚做完自定义res资源包的配置,这里做一下关于在配置过程中出现的问题和解决方法作一下记录。
资源的引用格式为:
@包名:资源类型/资源名
以framework资源为例:
@android:style/Theme.Holo.Light
这次需要配置与framework同级的资源包,以包名为"custemer"为例,配置完成后资源引用:
@custemer:style/Theme.Holo.Light
一、新建自定义资源包
- 在framework/base/core/下新建名为“res_custemer”文件夹,结构如下:
- 编写Android.mk/Android.bp文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# include apicheck.mk later, we need the build pass to prepare the first version
# include $(LOCAL_PATH)/apicheck.mk
LOCAL_PACKAGE_NAME := custemer-res
LOCAL_CERTIFICATE := platform
LOCAL_AAPT_FLAGS := -x3
# Tell aapt to build resource in utf16(the ROM will be enlarged),
# in order to save RAM size for string cache table
ifeq (yes,strip$(MTK_GMO_RAM_OPTIMIZE))
LOCAL_AAPT_FLAGS += --utf16
endif
LOCAL_NO_CUSTEMERRES := true
LOCAL_MODULE_TAGS := optional
# Install this alongside the libraries.
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
# Create package-export.apk, which other packages can use to get
# PRODUCT-agnostic resource data like IDs and type definitions.
LOCAL_EXPORT_PACKAGE_RESOURCES := true
include $(BUILD_PACKAGE)
# define a global intermediate target that other module may depend on.
.PHONY: gome-res-package-target
gome-res-package-target: $(LOCAL_BUILT_MODULE)
"LOCAL_AAPT_FLAGS := -x3 " value 的定义需要与 "frameworks\base\libs\androidfw\ResourceTypes.cpp"中 所定义的resource ID 一致
//
// Copyright (C) 2008 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
android_app {
name: "xxx-res",
certificate: "platform",
platform_apis: true,
// Soong special-cases framework-res to install this alongside
// the libraries at /system/framework/framework-res.apk.
// Generate private symbols into the com.android.internal.R class
// so they are not accessible to 3rd party apps.
aaptflags: [
// Framework doesn't need versioning since it IS the platform.
"--no-auto-version",
// Allow overlay to add resource
"--auto-add-overlay",
// Add for xxx
"--package-id 0x0a",
"--private-symbols",
"com.xxx.internal",
],
owner: "xxx",
// Create package-export.apk, which other packages can use to get
// PRODUCT-agnostic resource data like IDs and type definitions.
export_package_resources: true,
defaults: ["getxxxResManifest"],
}
bootstrap_go_package {
name: "soong-getxxxResManifest",
pkgPath: "android/soong/getxxxResManifest",
deps: [
"blueprint",
"blueprint-pathtools",
"soong",
"soong-android",
"soong-cc",
"soong-genrule",
"soong-apex",
],
srcs: [
"getxxxResManifest.go",
],
pluginFor: ["soong_build"],
}
getSunmiResManifest {
name: "getxxxResManifest",
}
- 编写AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="custemer" coreApp="true" android:sharedUserId="android.uid.system"
android:sharedUserLabel="@null">
<eat-comment />
<protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
<permission android:name="android.permission.ADVANCED_WIDGET_API"
android:protectionLevel="normal" />
<application android:process="system"
android:persistent="true"
android:hasCode="false"
android:label="@null"
android:allowClearUserData="false"
android:killAfterRestore="false"
android:icon="@null">
</application>
</manifest>
"custemer" 为资源包名
二、添加编译依赖关系
framework_custemer_res_package_export :=
framework_custemer_res_package_export_deps :=
ifneq ($(LOCAL_PACKAGE_NAME), mediatek-res)
framework_custemer_res_package_export += \
$(call intermediates-dir-for,APPS,mediatek-res,,COMMON)/package-export.apk
framework_custemer_res_package_export_deps += \
$(call intermediates-dir-for,APPS,mediatek-res,,COMMON)/src/R.stamp
ifneq ($(LOCAL_PACKAGE_NAME),custemer-res)
framework_custemer_res_package_export += \
$(call intermediates-dir-for,APPS,gome-res,,COMMON)/package-export.apk
framework_custemer_res_package_export_deps += \
$(call intermediates-dir-for,APPS,gome-res,,COMMON)/src/R.stamp
endif
endif
# FIXME: Cannot set in Android.mk due to base_rules.mk
#ifneq ($(LOCAL_PACKAGE_NAME),mediatek-res)
# LOCAL_RES_LIBRARIES += mediatek-res
#endif
....
$(filter-out $(framework_gome_res_package_export),$(resource_export_package)): $(framework_custemer_res_package_export_deps )
$(R_file_stamp): $(framework_custemer_res_package_export_deps )
$(resource_export_package) $(R_file_stamp) $(LOCAL_BUILT_MODULE): $(all_library_res_package_export_deps)
$(LOCAL_INTERMEDIATE_TARGETS): \
PRIVATE_AAPT_INCLUDES := $(all_library_res_package_exports) $(framework_custemer_res_package_export )
将 mediatek-res与自定义资源模块名定义在一起,否则编译时会出现circle dependence的错误;
将 framework_gome_res_package_export 添加进 PRIVATE_AAPT_INCLUDES 中,否则应用运行会报找不到资源的错误。
三、将资源路径添加进framework编译
路径: frameworks/base/Android.mk
framework_res_source_path := APPS/framework-res_intermediates/src
# M:add mediatek resource path
mediatek-res-source-path := APPS/mediatek-res_intermediates/src
gome_custemer_source_path := APPS/custemer-res_intermediates/src
...
LOCAL_INTERMEDIATE_SOURCES := \
$(framework_res_source_path)/android/R.java \
$(framework_res_source_path)/android/Manifest.java \
$(framework_res_source_path)/com/android/internal/R.java
# M:add mediatek resource R.java into framework,@{
LOCAL_INTERMEDIATE_SOURCES += \
$(mediatek-res-source-path)/com/mediatek/internal/R.java \
$(mediatek-res-source-path)/com/mediatek/R.java \
$(mediatek-res-source-path)/com/mediatek/Manifest.java
# @}
# M:add custemer resource R.java into framework,@{
LOCAL_INTERMEDIATE_SOURCES += \
$(custemer_res_source_path)/com/gome/internal/R.java \
$(custemer_res_source_path)/com/gome/R.java \
$(custemer_res_source_path)/com/gome/Manifest.java
# @}
LOCAL_MODULE := framework
# Make sure that R.java and Manifest.java are built before we build
# the source for this library.
framework_res_R_stamp := \
$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
$(full_classes_compiled_jar): $(framework_res_R_stamp)
$(built_dex_intermediate): $(framework_res_R_stamp)
# M:add mediatek resource dependes framework->mediatek_res->framework_res,@{
mediatek_res_R_stamp := \
$(call intermediates-dir-for,APPS,mediatek-res,,COMMON)/src/R.stamp
$(full_classes_compiled_jar): $(mediatek_res_R_stamp)
$(built_dex_intermediate): $(mediatek_res_R_stamp)
custemer_res_R_stamp := \
$(call intermediates-dir-for,APPS,custemer-res,,COMMON)/src/R.stamp
$(full_classes_compiled_jar): $(custemer_res_R_stamp)
$(built_dex_intermediate): $(custemer_res_R_stamp)
# @}
四、修改 PackageManagerService
路径:frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
ApplicationInfo mAndroidApplication;
/// M: [CIP] Add application info for mediatek-res.apk
ApplicationInfo mMediatekApplication;
ApplicationInfo mCustemerApplication;
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
/** M: [CIP] skip duplicated mediatek-res.apk @{ */
/// This will replace original resources with CIP resources
if (pkg.packageName.equals("com.mediatek")) {
synchronized (mPackages) {
if (mMediatekApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core mediatek package being redefined. Skipping.");
Slog.w(TAG, " file=" + scanFile);
Slog.w(TAG, "*************************************************");
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Core android package being redefined. Skipping.");
}
mMediatekApplication = pkg.applicationInfo;
}
}
/** @} */
if (pkg.packageName.equals("custemer")) {
synchronized (mPackages) {
if (mCustemerApplication != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core gome package being redefined. Skipping.");
Slog.w(TAG, " file=" + scanFile);
Slog.w(TAG, "*************************************************");
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Core android package being redefined. Skipping.");
}
mCustemerApplication = pkg.applicationInfo;
}
}
}
别的版本frameworks\base\services/core/java/com/android/server/pm/InstallPackageHelper.java
private void assertPackageIsValid(AndroidPackage pkg,
final @ParsingPackageUtils.ParseFlags int parseFlags,
final @PackageManagerService.ScanFlags int scanFlags)
throws PackageManagerException {
if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
ScanPackageUtils.assertCodePolicy(pkg);
}
if (pkg.getPath() == null) {
// Bail out. The resource and code paths haven't been set.
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
}
// Check that there is an APEX package with the same name only during install/first boot
// after OTA.
final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
// It is allowed to install a new APEX with the same name. But there shouldn't be
// conflicting names between APK and APEX.
final boolean installApex = (scanFlags & SCAN_AS_APEX) != 0;
if ((isUserInstall || isFirstBootOrUpgrade)
&& mPm.snapshotComputer().isApexPackage(pkg.getPackageName())
&& !installApex) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
pkg.getPackageName()
+ " is an APEX package and can't be installed as an APK.");
}
// Make sure we're not adding any bogus keyset info
final KeySetManagerService ksms = mPm.mSettings.getKeySetManagerService();
ksms.assertScannedPackageValid(pkg);
synchronized (mPm.mLock) {
// The special "android" package can only be defined once
if (pkg.getPackageName().equals("android")) {
if (mPm.getCoreAndroidApplication() != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
Slog.w(TAG, " codePath=" + pkg.getPath());
Slog.w(TAG, "*************************************************");
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Core android package being redefined. Skipping.");
}
}
if (pkg.getPackageName().equals("com.xxx")) {
if (mPm.getxxxApplication() != null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
Slog.w(TAG, " codePath=" + pkg.getPath());
Slog.w(TAG, "*************************************************");
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Core android package being redefined. Skipping.");
}
//mPm.mxxxApplication = pkg.toAppInfoWithoutState();
}
/// M: [CIP] skip duplicated mediatek-res.apk
mPm.sPmsExt.checkMtkResPkg(pkg);
// A package name must be unique; don't allow duplicates
if ((scanFlags & SCAN_NEW_INSTALL) == 0
&& mPm.mPackages.containsKey(pkg.getPackageName())) {
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
"Application package " + pkg.getPackageName()
+ " already installed. Skipping duplicate.");
}
if (pkg.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// but we still want the base name to be unique.
if ((scanFlags & SCAN_NEW_INSTALL) == 0
&& mPm.mPackages.containsKey(pkg.getManifestPackageName())) {
throw PackageManagerException.ofInternalError(
"Duplicate static shared lib provider package",
PackageManagerException.INTERNAL_ERROR_DUP_STATIC_SHARED_LIB_PROVIDER);
}
ScanPackageUtils.assertStaticSharedLibraryIsValid(pkg, scanFlags);
assertStaticSharedLibraryVersionCodeIsValid(pkg);
}
// If we're only installing presumed-existing packages, require that the
// scanned APK is both already known and at the path previously established
// for it. Previously unknown packages we pick up normally, but if we have an
// a priori expectation about this package's install presence, enforce it.
// With a singular exception for new system packages. When an OTA contains
// a new system package, we allow the codepath to change from a system location
// to the user-installed location. If we don't allow this change, any newer,
// user-installed version of the application will be ignored.
if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
if (mPm.isExpectingBetter(pkg.getPackageName())) {
Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
+ pkg.getPackageName());
} else {
PackageSetting known = mPm.mSettings.getPackageLPr(pkg.getPackageName());
if (known != null) {
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Examining " + pkg.getPath()
+ " and requiring known path " + known.getPathString());
}
if (!pkg.getPath().equals(known.getPathString())) {
throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
"Application package " + pkg.getPackageName()
+ " found at " + pkg.getPath()
+ " but expected at " + known.getPathString()
+ "; ignoring.");
}
} else {
throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Application package " + pkg.getPackageName()
+ " not found; ignoring.");
}
}
}
// Verify that this new package doesn't have any content providers
// that conflict with existing packages. Only do this if the
// package isn't already installed, since we don't want to break
// things that are installed.
if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
mPm.mComponentResolver.assertProvidersNotDefined(pkg);
}
// If this package has defined explicit processes, then ensure that these are
// the only processes used by its components.
ScanPackageUtils.assertProcessesAreValid(pkg);
// Verify that packages sharing a user with a privileged app are marked as privileged.
assertPackageWithSharedUserIdIsPrivileged(pkg);
// Apply policies specific for runtime resource overlays (RROs).
if (pkg.getOverlayTarget() != null) {
assertOverlayIsValid(pkg, parseFlags, scanFlags);
}
// Ensure the package is signed with at least the minimum signature scheme version
// required for its target SDK.
ScanPackageUtils.assertMinSignatureSchemeIsValid(pkg, parseFlags);
}
}
五、预置自定义资源包
目录:device/mediatek/common/device.mk
# for mediatek-res
PRODUCT_PACKAGES += mediatek-res
# for custemer-res
PRODUCT_PACKAGES += custemer-res
六、修改资源管理器 Assetmanager.cpp AssetManager.java
目录:frameworks\base\core\java\android\content\res\AssetManager.java
public final class AssetManager implements AutoCloseable {
private static final String TAG = "AssetManager";
private static final boolean DEBUG_REFS = false;
private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
private static final String MY_FRAMEWORK_APK_PATH = "/system/app/my-res/my-res.apk";
@GuardedBy("sSync")
@VisibleForTesting
public static void createSystemAssetsInZygoteLocked(boolean reinitialize,
String frameworkPath) {
if (sSystem != null && !reinitialize) {
return;
}
try {
final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
apkAssets.add(ApkAssets.loadFromPath(frameworkPath, ApkAssets.PROPERTY_SYSTEM));
final String[] systemIdmapPaths =
OverlayConfig.getZygoteInstance().createImmutableFrameworkIdmapsInZygote();
for (String idmapPath : systemIdmapPaths) {
apkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, ApkAssets.PROPERTY_SYSTEM));
}
apkAssets.add(ApkAssets.loadFromPath(MY_FRAMEWORK_APK_PATH, ApkAssets.PROPERTY_SYSTEM));
///M: for load mediatek-res
apkAssets.add(ApkAssets.loadFromPath(MEDIATEK_APK_PATH, ApkAssets.PROPERTY_SYSTEM /*system*/));
sSystemApkAssetsSet = new ArraySet<>(apkAssets);
sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
if (sSystem == null) {
sSystem = new AssetManager(true /*sentinel*/);
}
sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
} catch (IOException e) {
throw new IllegalStateException("Failed to create system AssetManager", e);
}
}
目录:framework/base/libs/androidfw/AssetManager.cpp
///M:add the resource path
static const char* kMediatekAssets = "/vendor/framework/mediatek-res/mediatek-res.apk";
///M:add the resource path
static const char* kCustemerAssets = "framework/custemer-res/custemer-res.apk";
bool AssetManager::addDefaultAssets()
{
const char* root = getenv("ANDROID_ROOT");
LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");
...
if((fp= fopen(pathCip,"w+"))!= NULL) {
.....
return isOK1;
} else {
//ALOGD("AssetManager-->addDefaultAssets CIP path not exsit!");
String8 path(root);
path.appendPath(kSystemAssets);
///M:add the new resource path into default path,so all the app can reference,@{
bool isOK1 = addAssetPath(path, NULL, false /* appAsLib */, true /* isSystemAsset */);
String8 path2(kMediatekAssets);
bool isOK2 = addAssetPath(path2, NULL, false, false);
String8 path3(root);
path3.appendPath(kCustemerAssets);
bool isOK3 =addAssetPath(path3, NULL, false, false);
if(!isOK3){
ALOGW("AssetManager-->addDefaultAssets isok3 is false");
}else{
ALOGW("AssetManager-->addDefaultAssets isok3 is true");
}
将自定义资源apk添加进系统资源管理器,当应用使用自定义资源id查找资源时,会通过AssetManager来查找。如果这里写错会导致会先各种问题。以字符串为例,当应用通过R.strings.xxx来查找字符串时,由于AssetManager无法返回正确的资源,默认会返回资源的id值。
六、修改 ResourceTypes.cpp,添加对应的package id
#define APP_PACKAGE_ID 0x7f
#define SYS_MTK_PACKAGE_ID 0x08 /// M: 0x08 is mediatek-res.apk resource ID
#define SYS_CUSTEMER_PACKAGE_ID 0x03 // 0x03 is gome-res.apk resource ID
#define SYS_PACKAGE_ID 0x01
bool ResTable::stringToValue(Res_value* outValue, String16* outString,
const char16_t* s, size_t len,
bool preserveSpaces, bool coerceType,
uint32_t attrID,
const String16* defType,
const String16* defPackage,
Accessor* accessor,
void* accessorCookie,
uint32_t attrType,
bool enforcePrivate) const
{
if (*s == '@') {
....
} else {
if (rid != 0) {
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId != APP_PACKAGE_ID && packageId != SYS_PACKAGE_ID && packageId != SYS_MTK_PACKAGE_ID && packageId != SYS_CUSTEMER_PACKAGE_ID) {
outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
}
outValue->data = rid;
return true;
}
if (accessor) {
uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name,
createIfNotFound);
if (rid != 0) {
if (kDebugTableNoisy) {
ALOGI("Pckg %s:%s/%s: 0x%08x\n",
String8(package).string(), String8(type).string(),
String8(name).string(), rid);
}
uint32_t packageId = Res_GETPACKAGE(rid) + 1;
if (packageId == 0x00) {
outValue->data = rid;
outValue->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
return true;
} else if (packageId == APP_PACKAGE_ID || packageId == SYS_PACKAGE_ID || packageId == SYS_MTK_PACKAGE_ID || packageId == SYS_CUSTEMER_PACKAGE_ID) {
// We accept packageId's generated as 0x01 in order to support
// building the android system resources
outValue->data = rid;
return true;
}
}
}
}
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
, mAppAsLib(appAsLib)
{
memset(mLookupTable, 0, sizeof(mLookupTable));
// Reserved package ids
mLookupTable[APP_PACKAGE_ID] = APP_PACKAGE_ID;
mLookupTable[SYS_PACKAGE_ID] = SYS_PACKAGE_ID;
mLookupTable[SYS_MTK_PACKAGE_ID] = SYS_MTK_PACKAGE_ID;
mLookupTable[SYS_CUSTEMER_PACKAGE_ID] = SYS_CUSTEMER_PACKAGE_ID;
}
}
SYS_CUSTEMER_PACKAGE_ID 需要对应与自定义模块mk文件的 LOCAL_AAPT_FLAGS
七、将资源apk路径添加进白名单
路径:frameworks/base/core/jni
static const char* kPathWhitelist[] = {
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
"/system/etc/event-log-tags",
"/sys/kernel/debug/tracing/trace_marker",
"/system/framework/framework-res.apk",
"/dev/urandom",
"/dev/ion",
"/dev/dri/renderD129", // Fixes b/31172436
"/proc/ged", // MTK add
"/system/vendor/framework/mediatek-res/mediatek-res.apk", // MTK add
"/system/framework/custemer-res/custemer-res.apk" /* XXX add */
};
修改的目的是为了通过开机的检查,否则无法开机。
关于如何使用,就不作记录了。