• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android Framework 的AOSP :系统 App 源码

武飞扬头像
阿豪元代码
帮助624

1. 如何新建一个系统 App 项目

使用 Android Studio 新建一个空项目 FirstSystemApp,包名设置为 com.yuandaima.firstsystemapp,语言选择 Java。后面为叙述方便称该项目为 as 项目。

接着在 Jelly/Rice14 目录下创建如下的目录和文件:

接着将 as 项目中的 res 文件下的资源文件拷贝到 Jelly/Rice14/FirstSystemApp/res 中,把 as 项目中的 MainActivity.java 拷贝到 Jelly/Rice14/FirstSystemApp/src/com/yuandaima/firstsystemapp 中。

接着修改已添加的 AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yuandaima.firstsystemapp">

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.FirstSystemApp">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:name="android.app.lib_name"
                android:value="" />
        </activity>
    </application>

</manifest>

接着修改已添加的 Android.bp 文件:

android_app {
    name: "FirstSystemApp",

    srcs: ["src/**/*.java"],

    resource_dirs: ["res"],

    manifest: "AndroidManifest.xml",

    sdk_version: "current",

    certificate: "platform",

    product_specific: true,

    //依赖
    static_libs: ["androidx.appcompat_appcompat",
                 "com.google.android.material_material",
                 "androidx-constraintlayout_constraintlayout"],

}

至此我们的系统 App 就创建好了。

接着在我们的 Product 中添加这个App,修改 device/Jelly/Rice14/Rice14.mk

# 添加以下内容
PRODUCT_PACKAGES  = FirstSystemApp

接着编译系统,启动虚拟机,打开 app:

source build/envsetup.sh
lunch Rice14-eng
make -j16
emulator 

2. 系统 App 与 普通 App 的差异

2.1 系统 App 可以使用更多的 api

当我们在 Android.bp 中配置了:

platform_apis: true,
sdk_version: "",

当 platform_apis 为 true 时,sdk_version 必须为空。这种情况下我们的 app 会使用平台 API 进行编译而不是 SDK,这样我们的 App 就能访问到非 SDK API 了。关于 SDK API 和非 SDK API 的内容可以参考官方文档

2.2 系统 App 的签名

AOSP 内置了 APK 签名文件,我们可以在 Android.bp 中通过 certificate 配置系统 app 的签名文件,certificate 的值主要有一下几个选项:

  • testkey:普通 APK,默认情况下使用
  • platform:该 APK 完成一些系统的核心功能。经过对系统中存在的文件夹的访问测试,这种方式编译出来的 APK 所在进程的 UID 为system
  • shared:该 APK 需要和 home/contacts 进程共享数据
  • media:该 APK 是 media/download 系统中的一环
  • PRESIGNED:表示 这个 apk 已经签过名了,系统不需要再次签名;

2.3 系统 App 能使用更多的权限

当 Android.bp 中的 privileged 被配置为 true 时,我们的系统 App 在添加特许权限许可名单后,能使用 signature 和 signatureOrSystem 级别的权限,而普通 App 是不能使用这些权限的。

在后续的系统权限相关的分享中会介绍如何给系统 App 添加特许权限许可名单

3. 系统 App 添加依赖

1. 添加 AOSP 中已有的库

在 FirstSystemApp 的 Android.bp 中我们添加了很多依赖:

    static_libs: ["androidx.appcompat_appcompat",
                 "com.google.android.material_material",
                 "androidx-constraintlayout_constraintlayout"],

在 AOSP 中, 很多常用的库均以预编译模块的方式添加到系统源码中。比如常用的 AndroidX 库定义在 prebuilts/sdk/current/androidx 目录下。这些库通过 prebuilts/sdk/current/androidx/Android.bp 引入。比如 recyclerview 库的引入方式如下:

android_library {
    name: "androidx.recyclerview_recyclerview",
    sdk_version: "31",
    apex_available: [
        "//apex_available:platform",
        "//apex_available:anyapex",
    ],
    min_sdk_version: "14",
    manifest: "manifests/androidx.recyclerview_recyclerview/AndroidManifest.xml",
    static_libs: [
        "androidx.recyclerview_recyclerview-nodeps",
        "androidx.annotation_annotation",
        "androidx.collection_collection",
        "androidx.core_core",
        "androidx.customview_customview",
    ],
    java_version: "1.7",
}

可以看到引入的是一个 android_library,名字叫 androidx.recyclerview_recyclerview。maifest 文件在 manifests/androidx.recyclerview_recyclerview/ 目录下,进入这个目录只有一个 AndroidManifest.xml 文件,其内容如下:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="androidx.recyclerview" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="28" />

</manifest>

很奇怪,并没有看到 RecyclerView 库的源码,也没有看到 aar 库文件。我们接着看 Android.bp 中的依赖,其中一项是 androidx.recyclerview_recyclerview-nodeps,我们在 Android.bp 中看一下它的引入方式:

android_library_import {
    name: "androidx.recyclerview_recyclerview-nodeps",
    aars: ["m2repository/androidx/recyclerview/recyclerview/1.1.0-alpha07/recyclerview-1.1.0-alpha07.aar"],
    sdk_version: "current",
    min_sdk_version: "14",
    static_libs: [
        "androidx.annotation_annotation",
        "androidx.collection_collection",
        "androidx.core_core",
        "androidx.customview_customview",
    ],
}

这里看到了,它的 aar 库在这里: m2repository/androidx/recyclerview/recyclerview/1.1.0-alpha07/recyclerview-1.1.0-alpha07.aar

继续查阅我们可以发现,prebuilts/tools/common/m2 目录下引入了大量的三方库。

总结一下,当我们的系统 App 需要引入一个库的时候,通常会在 prebuilds 目录下查找:

  • androidx 相关库引入,先在 prebuilts/sdk/current/androidx 下寻找配置好的 bp 文件
  • 其他库引入,先在 prebuilts/tools/common/m2 下寻找寻找配置好的 bp 文件

都没有,就得自己引入了

2. 自己给 AOSP 添加库

java 库源码引入

这部分参考之前分享的玩转 AOSP 之自定义模块添加添加自定义模块之 Java 库和可执行程序章节

java 库以 jar 包形式引入

这部分参考之前分享的玩转 AOSP 之预编译模块添加JAR 包章节

Android 库源码引入

device/Jelly/Rice14 目录下创建如下的文件和文件夹

其中 MyCustomView.java 是一个用于演示的没有具体功能的自定义 View:

package com.yuandaima.firstsystemandroidlibrary;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class MyCustomView extends View {
    public MyCustomView(Context context) {
        super(context);
    }

    public MyCustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public MyCustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}

AndroidManifest.xml 的内容如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.yuandaima.firstsystemandroidlibrary">

</manifest>

Android.bp 的内容如下:

android_library  {

    //......
    
    //依赖
    static_libs: ["androidx.appcompat_appcompat",
                 "com.google.android.material_material",
                 "androidx-constraintlayout_constraintlayout",
                 "FirstSystemAndroidLibrary"],

}

修改一下 MainActivity,在 App 里使用我们的自定义 View:

package com.yuandaima.firstsystemapp;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import com.yuandaima.firstsystemandroidlibrary.MyCustomView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyCustomView myView = new MyCustomView(this);
    }
}

接着编译系统,启动虚拟机,打开 app:

source build/envsetup.sh
lunch Rice14-eng
make -j16
emulator 

这样我们的库就算引入完毕了。

Android 库以 aar 包形式引入

更多的时候 Android 库是以 aar 包的形式引入。

假设我们的 SourceApp 需要引入 lottie 这个动画库。

首先我们这里下载好 lottie 库的 aar 打包文件。

device/Jelly/Rice14 目录下创建如下的目录结构:

liblottie/
├── Android.bp
└── lottie-5.2.0.aar

其中 Android.bp 的内容如下:

android_library_import {
    name: "lib-lottie",
    aars: ["lottie-5.2.0.aar"],
    sdk_version: "current",
}

然后我们修改 FirstSystemApp 中的 Android.bp 引入这个库:


    static_libs: ["androidx.appcompat_appcompat",
                 "com.google.android.material_material",
                 "androidx-constraintlayout_constraintlayout",
                 "FirstSystemAndroidLibrary",
                  "lib-lottie"],

这样就可以在 App 中使用 lottie 库了

JNI 项目

Android 10 下,Android.bp(soong) 方式对 JNI 的支持有点问题,所以我们只有用 Android.mk 来演示了。Android 13 下 Android.bp (soong) 是完美支持 JNI 的。

device/Jelly/Rice14 目录下添加如下的文件与文件夹:

jni/Android.mk 内容如下:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

# This is the target being built.
LOCAL_MODULE:= myjnilib


# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
    native.cpp

# All of the shared libraries we link against.
LOCAL_LDLIBS := -llog

# No static libraries.
LOCAL_STATIC_LIBRARIES :=

LOCAL_CFLAGS := -Wall -Werror

LOCAL_NDK_STL_VARIANT := none

LOCAL_SDK_VERSION := current

LOCAL_PRODUCT_MODULE := true

include $(BUILD_SHARED_LIBRARY)

jni/native.cpp 的内容如下:

/*
 * 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.
 */

#define LOG_TAG "simplejni native.cpp"
#include <android/log.h>

#include <stdio.h>

#include "jni.h"

#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

static jint
add(JNIEnv* /*env*/, jobject /*thiz*/, jint a, jint b) {
int result = a   b;
    ALOGI("%d   %d = %d", a, b, result);
    return result;
}

static const char *classPathName = "com/example/android/simplejni/Native";

static JNINativeMethod methods[] = {
  {"add", "(II)I", (void*)add },
};

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = env->FindClass(className);
    if (clazz == NULL) {
        ALOGE("Native registration unable to find class '%s'", className);
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        ALOGE("RegisterNatives failed for '%s'", className);
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

/*
 * Register native methods for all classes we know about.
 *
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv* env)
{
  if (!registerNativeMethods(env, classPathName,
                 methods, sizeof(methods) / sizeof(methods[0]))) {
    return JNI_FALSE;
  }

  return JNI_TRUE;
}


// ----------------------------------------------------------------------------

/*
 * This is called by the VM when the shared library is first loaded.
 */
 
typedef union {
    JNIEnv* env;
    void* venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/)
{
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv* env = NULL;
    
    ALOGI("JNI_OnLoad");

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("ERROR: GetEnv failed");
        goto bail;
    }
    env = uenv.env;

    if (registerNatives(env) != JNI_TRUE) {
        ALOGE("ERROR: registerNatives failed");
        goto bail;
    }
    
    result = JNI_VERSION_1_4;
    
bail:
    return result;
}

SimpleJNI.java 的内容如下:


package com.example.android.simplejni;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class SimpleJNI extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        int sum = Native.add(2, 3);
        tv.setText("2   3 = "   Integer.toString(sum));
        setContentView(tv);
    }
}

class Native {
    static {
    	// The runtime will add "lib" on the front and ".o" on the end of
    	// the name supplied to loadLibrary.
        System.loadLibrary("simplejni");
    }

    static native int add(int a, int b);
}

最外面的 Android.mk 的内容如下:

TOP_LOCAL_PATH:= $(call my-dir)

# Build activity

LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := JNIApp

LOCAL_JNI_SHARED_LIBRARIES := myjnilib

LOCAL_PROGUARD_ENABLED := disabled

LOCAL_SDK_VERSION := current

LOCAL_DEX_PREOPT := false

LOCAL_PRODUCT_MODULE := true

include $(BUILD_PACKAGE)

# ============================================================

# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))

AndroidManifest.xml 的内容如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.android.simplejni">
    <application android:label="Simple JNI">
        <activity android:name="SimpleJNI">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest> 

最后在 device/Jelly/Rice14/Rice14.mk 中添加:

PRODUCT_PACKAGES  = helloworld \
    JNIApp \

编译并运行虚拟机就可以看到 JNIApp 了:

JNIApp 链接自定义库

我们这里尝试修改 JNIApp,让其引用到我们的 libmymath 库。

修改 JNIApp/jni/Android.mk:

# 添加以下内容
LOCAL_SHARED_LIBRARIES := libmymath

修改 JNIApp/jni/native.cpp:

#include "my_math.h"

static jint
add(JNIEnv* /*env*/, jobject /*thiz*/, jint a, jint b) {
    int result = a   b;
    result = my_add(result, result);
    ALOGI("%d   %d = %d", a, b, result);
    return result;
}

然后编译系统,发现报以下错误:

error: myjnilib (native:ndk:none:none) should not link to libmymath (native:platform)

可以看出是编译平台不一致导致的,修改 JNIApp/jni/Android.mk:

# 下面这行注释掉即可
# LOCAL_SDK_VERSION := current

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanfekic
系列文章
更多 icon
同类精品
更多 icon
继续加载