最近在使用google 平板 Tangorpro来研究安卓Framework,因此刷成了可以自己编译的AOSP版本.

但是AOSP 开源代码没有自带Google Play应用商店, 通过查询,有通过 OpenGApp的方式来集成.

参考:  https://github.com/opengapps/aosp_build

我的需求只需要集成三件套(Google服务框架、Google play商店和Google Play服务), 能从Google应用市场下载安装应用即可.

有没有更简单的集成方法呢?

第一个问题就是要下载一个跟本地设备匹配的APK版本, 这个可以从Tangorpro的原生ROM中提取出来.

可以从https://developers.google.com/android/drivers?hl=zh-cn 下载本设备对应的镜像文件.

把镜像里的三件套APK提取出来即可.

Image 路径 文件 包名
product.img /product/priv-app/Phonesky Phonesky.apk com.android.vending
product.img /product/priv-app/PrebuiltGmsCoreSc PrebuiltGmsCoreSc.apk com.google.android.gms
system_ext.img /system_ext/priv-app/GoogleServicesFramework GoogleServicesFramework.apk com.google.android.gsf

有了这个三个apk, 如何集成到ROM里呢?

可以参考vendor/google_devices/tangorpro/proprietary下面UwbVendorService.apk的配置方法,

将三个APK拷贝到这个目录:

修改此目录下面的Android.mk,末尾增加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
include $(CLEAR_VARS)
LOCAL_MODULE := Phonesky
LOCAL_PACKAGE_NAME := com.android.vending
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_OWNER := qorvo
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
include $(BUILD_PREBUILT)



include $(CLEAR_VARS)
LOCAL_MODULE := GoogleServicesFramework
LOCAL_PACKAGE_NAME := com.google.android.gsf
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_OWNER := qorvo
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
include $(BUILD_PREBUILT)



include $(CLEAR_VARS)
LOCAL_MODULE := PrebuiltGmsCoreSc
LOCAL_PACKAGE_NAME := com.google.android.gms
LOCAL_SYSTEM_EXT_MODULE := true
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_OWNER := qorvo
LOCAL_MODULE_CLASS := APPS
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
include $(BUILD_PREBUILT)

再把上级目录的device-partial.mk 修改一下,

1
2
3
4
5
PRODUCT_PACKAGES := \
UwbVendorService \
Phonesky \
GoogleServicesFramework \
PrebuiltGmsCoreSc

注意,重新make时需要增加一个设置export DISABLE_ARTIFACT_PATH_REQUIREMENTS=true

否则编译会报错:

1
2
3
4
5
6
7
8
9
10
vendor/google_devices/tangorpro/device-partial.mk was modified, regenerating...
vendor/google_devices/tangorpro/device-partial.mk was modified, regenerating...
[ 99% 144/145] finishing legacy Make module parsing ...
FAILED:
build/make/core/artifact_path_requirements.mk:31: warning: device/google/tangorpro/aosp_tangorpro.mk produces files inside build/make/target/product/generic_system.mks artifact path requirement.
Offending entries:
system/priv-app/Phonesky/Phonesky.apk
In file included from build/make/core/main.mk:1407:
build/make/core/artifact_path_requirements.mk:31: error: Build failed.
13:03:53 ckati failed with: exit status 1

重新make ,烧录.重启.

设置科学上网之后,打开Google Play会提示需要认证设备. Play保护机制认证 可以通过Google的临时授权来加入,参考:

https://www.google.com/android/uncertified/

将此设备的ID加入临时设备注册即可. 等待5分钟左右即可生效.

如果一直提示未获取Play保护机制认证, 可以清空Google Play和Goole服务的数据,再重试.

点击登录,报错,日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
E  FATAL EXCEPTION: main
Process: com.android.vending, PID: 9176
java.lang.RuntimeException: Unable to start service com.google.android.finsky.setup.VpaService@14bce4b with Intent { dat=vpaservice://startvpafordeferredsetupnotification/... cmp=com.android.vending/com.google.android.finsky.setup.VpaService }: java.lang.SecurityException: Starting FGS with type systemExempted callerApp=ProcessRecord{119f011 9176:com.android.vending/u0a128} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED] any of the permissions allOf=false [android.permission.SCHEDULE_EXACT_ALARM, android.permission.USE_EXACT_ALARM, android:activate_vpn]
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5100)
at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2432)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:232)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8592)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
Caused by: java.lang.SecurityException: Starting FGS with type systemExempted callerApp=ProcessRecord{119f011 9176:com.android.vending/u0a128} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED] any of the permissions allOf=false [android.permission.SCHEDULE_EXACT_ALARM, android.permission.USE_EXACT_ALARM, android:activate_vpn]
at android.os.Parcel.createExceptionOrNull(Parcel.java:3183)
at android.os.Parcel.createException(Parcel.java:3167)
at android.os.Parcel.readException(Parcel.java:3150)
at android.os.Parcel.readException(Parcel.java:3092)
at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:6960)
at android.app.Service.startForeground(Service.java:776)
at com.google.android.finsky.setup.VpaService.onStartCommand(PG:112)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5082)
at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(Unknown Source:0
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2432
at android.os.Handler.dispatchMessage(Handler.java:107
at android.os.Looper.loopOnce(Looper.java:232
at android.os.Looper.loop(Looper.java:317
at android.app.ActivityThread.main(ActivityThread.java:8592
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActiveServices.validateForegroundServiceType(ActiveServices.java:2842)
at com.android.server.am.ActiveServices.setServiceForegroundInnerLocked(ActiveServices.java:2530)
at com.android.server.am.ActiveServices.setServiceForegroundLocked(ActiveServices.java:1806)
at com.android.server.am.ActivityManagerService.setServiceForeground(ActivityManagerService.java:13793)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:3483)

查看ActiveServices.java 的函数validateForegroundServiceType

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private Pair<Integer, RuntimeException> validateForegroundServiceType(ServiceRecord r,
@ForegroundServiceType int type,
@ForegroundServiceType int defaultToType,
@ForegroundServiceType int startType) {
final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy();
final ForegroundServiceTypePolicyInfo policyInfo =
policy.getForegroundServiceTypePolicyInfo(type, defaultToType);
final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy(
mAm.mContext, r.packageName, r.app.uid, r.app.getPid(),
r.isFgsAllowedWiu_forStart(), policyInfo);
RuntimeException exception = null;
switch (code) {
case FGS_TYPE_POLICY_CHECK_DEPRECATED: {
final String msg = "Starting FGS with type "
+ ServiceInfo.foregroundServiceTypeToLabel(type)
+ " code=" + code
+ " callerApp=" + r.app
+ " targetSDK=" + r.app.info.targetSdkVersion;
Slog.wtfQuiet(TAG, msg);
Slog.w(TAG, msg);
} break;
case FGS_TYPE_POLICY_CHECK_DISABLED: {
if (startType == FOREGROUND_SERVICE_TYPE_MANIFEST
&& type == FOREGROUND_SERVICE_TYPE_NONE) {
exception = new MissingForegroundServiceTypeException(
"Starting FGS without a type "
+ " callerApp=" + r.app
+ " targetSDK=" + r.app.info.targetSdkVersion);
} else {
exception = new InvalidForegroundServiceTypeException(
"Starting FGS with type "
+ ServiceInfo.foregroundServiceTypeToLabel(type)
+ " callerApp=" + r.app
+ " targetSDK=" + r.app.info.targetSdkVersion
+ " has been prohibited");
}
} break;
case FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_PERMISSIVE: {
final String msg = "Starting FGS with type "
+ ServiceInfo.foregroundServiceTypeToLabel(type)
+ " code=" + code
+ " callerApp=" + r.app
+ " targetSDK=" + r.app.info.targetSdkVersion
+ " requiredPermissions=" + policyInfo.toPermissionString()
+ (policyInfo.hasForegroundOnlyPermission()
? " and the app must be in the eligible state/exemptions"
+ " to access the foreground only permission" : "");
Slog.wtfQuiet(TAG, msg);
Slog.w(TAG, msg);
} break;
case FGS_TYPE_POLICY_CHECK_PERMISSION_DENIED_ENFORCED: {
exception = new SecurityException("Starting FGS with type "
+ ServiceInfo.foregroundServiceTypeToLabel(type)
+ " callerApp=" + r.app
+ " targetSDK=" + r.app.info.targetSdkVersion
+ " requires permissions: "
+ policyInfo.toPermissionString()
+ (policyInfo.hasForegroundOnlyPermission()
? " and the app must be in the eligible state/exemptions"
+ " to access the foreground only permission" : ""));
exception.printStackTrace();
} break;
case FGS_TYPE_POLICY_CHECK_OK:
default:
break;
}
return Pair.create(code, exception);
}

是由于policy.checkForegroundServiceTypePolicy 检测未过.

ForegroundServiceTypePolicy.checkForegroundServiceTypePolicy方法又会调用

SystemExemptedFgsTypePermission.checkPermission方法,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private class SystemExemptedFgsTypePermission extends ForegroundServiceTypePermission {
SystemExemptedFgsTypePermission() {
super("System exempted");
}

@Override
public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
@NonNull String packageName, boolean allowWhileInUse) {
final AppRestrictionController appRestrictionController = mAm.mAppRestrictionController;
@ReasonCode int reason = appRestrictionController
.getPotentialSystemExemptionReason(callerUid);
if (reason == REASON_DENIED) {
reason = appRestrictionController
.getPotentialSystemExemptionReason(callerUid, packageName);
if (reason == REASON_DENIED) {
reason = appRestrictionController
.getPotentialUserAllowedExemptionReason(callerUid, packageName);
}
}
if (reason == REASON_DENIED) {
if (ArrayUtils.contains(mAm.getPackageManagerInternal().getKnownPackageNames(
KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM), packageName)) {
reason = REASON_PACKAGE_INSTALLER;
}
}

switch (reason) {
case REASON_SYSTEM_UID:
case REASON_SYSTEM_ALLOW_LISTED:
case REASON_DEVICE_DEMO_MODE:
case REASON_DISALLOW_APPS_CONTROL:
case REASON_DEVICE_OWNER:
case REASON_PROFILE_OWNER:
case REASON_PROC_STATE_PERSISTENT:
case REASON_PROC_STATE_PERSISTENT_UI:
case REASON_SYSTEM_MODULE:
case REASON_CARRIER_PRIVILEGED_APP:
case REASON_DPO_PROTECTED_APP:
case REASON_ACTIVE_DEVICE_ADMIN:
case REASON_ROLE_EMERGENCY:
case REASON_ALLOWLISTED_PACKAGE:
case REASON_PACKAGE_INSTALLER:
case REASON_SYSTEM_EXEMPT_APP_OP:
return PERMISSION_GRANTED;
default:
return PERMISSION_DENIED;
}
}
}

AppRestrictionController.getPotentialSystemExemptionReason方法代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* @param uid The uid to check.
* @return The potential exemption reason of the given uid. The caller must decide
* whether or not it should be exempted.
*/
@ReasonCode
int getPotentialSystemExemptionReason(int uid) {
if (UserHandle.isCore(uid)) {
return REASON_SYSTEM_UID;
}
if (isOnSystemDeviceIdleAllowlist(uid)) {
return REASON_SYSTEM_ALLOW_LISTED;
}
if (UserManager.isDeviceInDemoMode(mContext)) {
return REASON_DEVICE_DEMO_MODE;
}
final int userId = UserHandle.getUserId(uid);
if (mInjector.getUserManagerInternal()
.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL, userId)) {
return REASON_DISALLOW_APPS_CONTROL;
}
final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
if (am.isDeviceOwner(uid)) {
return REASON_DEVICE_OWNER;
}
if (am.isProfileOwner(uid)) {
return REASON_PROFILE_OWNER;
}
final int uidProcState = am.getUidProcessState(uid);
if (uidProcState <= PROCESS_STATE_PERSISTENT) {
return REASON_PROC_STATE_PERSISTENT;
} else if (uidProcState <= PROCESS_STATE_PERSISTENT_UI) {
return REASON_PROC_STATE_PERSISTENT_UI;
}
return REASON_DENIED;
}

分析SystemExemptedFgsTypePermission .checkPermission, 发现是判断当前用户id是不是可以系统豁免没有通过.

是不是可以修改包名,伪装成REASON_PACKAGE_INSTALLER呢?

修改代码如下,直接将com.android.vending判断为REASON_PACKAGE_INSTALLER

1
2
3
4
5
6
7
if (ArrayUtils.contains(mAm.getPackageManagerInternal().getKnownPackageNames(
KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM), packageName)||

"com.android.vending".equals(packageName)
) {
reason = REASON_PACKAGE_INSTALLER;
}

重新编译烧录之后,可以登录Google Play,安装下载也是正常的.