精华内容
下载资源
问答
  • Android overlay和预制apk

    2020-11-18 18:01:37
    一.Android overlay 多个overlay的优先级判定 在mk文件中通过定义PRODUCT_PACKAGE_OVERLAYS或DEVICE_PACKAGE_OVERLAYS变量,后面可以加上多个overlay目录路径,以此来实现多个overlay目录。但是这些目录是有优先级...

    一.Android overlay

    多个overlay的优先级判定

    在mk文件中通过定义PRODUCT_PACKAGE_OVERLAYS或DEVICE_PACKAGE_OVERLAYS变量,后面可以加上多个overlay目录路径,以此来实现多个overlay目录。但是这些目录是有优先级顺序的,PRODUCT_PACKAGE_OVERLAYS下的目录优先级高于DEVICE_PACKAGE_OVERLAYS下目录的优先级,写在前面的目录优先级高于写在后面目录的优先级,举个例子:

    PRODUCT_PACKAGE_OVERLAYS = overlay_A overlay_B
    DEVICE_PACKAGE_OVERLAYS = overlay_C overlay_D
    

    上述overlay目录优先级顺序:overlay_A >overlay_B> overlay_C >overlay_D

    PRODUCT_PACKAGE_OVERLAYS:用于一个指定的产品,即某个品牌的某个型号。
    DEVICE_PACKAGE_OVERLAYS: 用于某个品牌的所有产品。
    一般”device/” 路径下的overlay使用DEVICE_PACKAGE_OVERLAYS,而”vendor/”路径下的overlay使用RODUCT_PACKAGE_OVERLAYS。

    overlay资源替换的前提和原则

    前提:资源所在路径必须与overlay下资源路径完全相同。如要替换 Settings 这个应用的String.xml里的资源,该资源文件所在路径为packages/apps/res/values/,则对应overlay的路径必须为overlay/packages/apps/res/values/。
    原则:overlay替换的是资源,不是文件。举个例子,应用中String.xml里的内容如下:

    <String name="a">aaa</String>
    <String name="b">bbb</String>
    <String name="c">ccc</String>
    

    overlay中的String.xml里的内容如下:

    <String name="a">abc</String>
    

    则最终,apk调用的资源如下:

    <String name="a">abc</String>
    <String name="b">bbb</String>
    <String name="c">ccc</String>
    

    而不是想象中的

    <String name="a">abc</String>
    
    注意,关于替换,查询相关英文文档发现有如下描述

    For color, bool, string, array, style/theme types, the resource values are identifed by their keys, so for these types, there is no need to put the resources in a file with the same name as in the original base package.

    For layout, animation, picture drawables and raw types, the resources are indentifed by their file name, and overlay for these resources should keep the file name same as in the base packages.

    翻译成中文的大概意思就是:

    1.对于color,bool,String,array,style等资源的值是有他们的键确定的,比如
    <String name="a">abc</String>
    

    该字符串资源通过键 name = “a” 来唯一确定值 abc 。也就是说,overlay里的这类资源文件的文件名不需要与应用包里的资源文件的文件名保持一致。只需要,资源文件里的键保持一致就行了。

    2.对于布局文件,动画文件,图片资源文件等,这些资源文件是通过文件名来唯一确定,所以overlay里的这类资源文件需要与应用包里的资源文件的文件名保持一致。

    但为了不必要的麻烦,还是尽量保持一致

    Android 系统 overlay 机制重点小结

    静态 overlay 和 动态 overlay

    静态 overlay:又称为编译时 overlay,编译时资源就已经覆盖了,一般用在有源码的apk中。
    动态 overlay:又称为运行时 overlay,当 apk 在手机中运行时才发生资源覆盖,一般用在无源码的apk中

    静态 overlay
    1.修改产品中mk文件指定overlay路径

    例如:在/home/android/xxx/android/vendor/xxx/trunk/configs.mk文件中添加

    PRODUCT_PACKAGE_OVERLAYS += $(LOCAL_PATH)/overlay
    
    2.在overlay目录下创建资源文件

    例如:在/home/android/xxx/android/vendor/xxx/trunk/overlay/packages/apps/Settings/res/values/config.xml

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    
        <!-- Whether to support CT PA requirement or not -->
        <bool name="config_support_CT_PA">false</bool>
        <!-- Whether screen_pinning_settings should be shown or not. -->
        <bool name="config_show_screen_pinning_settings">false</bool>
    </resources>
    

    又或者/home/android/xxx/android/vendor/xxx/trunk/overlay/packages/apps/Settings/res/values/strings.xml

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <string name="zzz_certification_settings_title">Certification</string>
        <string name="ringtone_title1">Phone ringtone SIM1</string>
        <string name="ringtone_title2">Phone ringtone SIM2</string>
    </resources>
    
    动态 overlay

    RRO能直接定制替换第三方APK的资源,而不需要其源码。RRO的编译结果会得到一个xxx_overlay.apk。RRO不能替换AndroidManifest.xml文件及reference resource 类型的文件,如layout、anim、xml目录中的xml文件。虽然RRO具有自己的AndroidManifest.xml文件,但它却不能替换源项目中的AndroidManifest.xml文件。关于layout目录中的xml文件,SRO是可以替换的。

    1.修改产品中mk文件指定overlay路径

    因为要生成apk文件,所以要把新建目录中的Android.mk文件给包含进去
    例如:在/home/android/xxx/android/vendor/xxx/trunk/configs.mk添加

    $(call inherit-product-if-exists, $(LOCAL_PATH)/rroOverlay/rrooverlay.mk)
    

    inherit-product-if-exists主要是打一些标记,记录继承关系
    Android Makefile中inherit-product函数简介,以及与include的区别

    例如:/home/android/xxx/android/vendor/xxx/trunk/rroOverlay/rrooverlay.mk

    LOCAL_PATH := vendor/tinno/$(TARGET_PRODUCT)/$(PROJECT_NAME)/packages/apps/
    PRODUCT_PACKAGES += \
        WallpaperPickerClaOverlay \
        FrameworkResClaOverlay \
        GmsSampleIntegrationClaOverlay
    
    2.在RRO的包中添加文件

    例如:/home/android/xxx/android/vendor/xxx/trunk/rroOverlay/WallpaperPickerClaOverlay/AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.android.wallpaperpicker.overlay">
        <overlay 
        android:targetPackage="com.android.wallpaperpicker"
        android:priority="1"
        android:isStatic="true"/>
    </manifest>
    

    android:targetPackage 要与被rro应用包名一直一致
    android:priority=“1” 覆盖优先级,值越大优先级越高
    把app放到vendor/overlay下面以后,还要activate,默认是disable的
    activate有两种方式:
    一种是overlay的app的清单文件中,overlay标签写 isStatic=“true”
    一种是加系统属性ro.boot.vendor.overlay.theme=overlay的app的包名,分号分割

    例如:/home/android/xxx/android/vendor/xxx/trunk/rroOverlay/WallpaperPickerClaOverlay/Android.mk

    LOCAL_PATH := $(call my-dir)
    $(warning mike LOCAL_PATH = $(LOCAL_PATH))
    
    include $(CLEAR_VARS)
    LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res#包含res资源目录
    
    $(warning mike LOCAL_RESOURCE_DIR = $(LOCAL_RESOURCE_DIR))
    LOCAL_MANIFEST_FILE := AndroidManifest.xml#指定加载AndroidManifest文件位置,可以不指定,默认是这个路径
    #optional:指该模块在所有版本下都编译;user:指该模块只在user版本下才编译;eng:指该模块只在eng版本下才编译
    LOCAL_MODULE_TAGS := optional
    LOCAL_SDK_VERSION := current
    #除应用(apk)以LOCAL_PACKAGE_NAME指定模块名以外,其余的模块都以LOCAL_MODULE指定模块名。
    LOCAL_PACKAGE_NAME := WallpaperPickerClaOverlay#指定apk名称
    LOCAL_MODULE_PATH := $(PRODUCT_OUT)/carrier_600CLA/overlay#指定编译后路径,即目标的安装路径
    #自动添加overlays包里的资源
    LOCAL_AAPT_FLAGS := --auto-add-overlay#用于新增资源,不覆盖原有软件包中的资源
    #Android Asset Packaging Tool:aapt是Android资源打包工具,aapt2是优化过的aapt
    LOCAL_USE_AAPT2 := false
    LOCAL_AAPT_INCLUDE_ALL_RESOURCES := true
    include $(BUILD_PACKAGE)#建立一个APK
    

    LOCAL_AAPT_FLAGS := --auto-add-overlay#用于新增资源,不覆盖原有软件包中的资源

    然后在/home/android/xxx/android/vendor/xxx/trunk/rroOverlay/WallpaperPickerClaOverlay目录下添加要RRO的目录

    通过 adb shell dumpsys overlay 命令可以查看系统里面的overlay包以及包的状态
    [FAQ23392] RRO framework-res

    通过aapt overlay

    AAPT(Android Asset Packaging Tool) 在SDK的tools/目录下. 该工具可以查看, 创建, 更新ZIP格式的文档附件(zip, jar, apk). 也可将资源文件编译成二进制文件

    添加文件到打包好的apk中
    aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]
    

    解释:aapt a <你的apk文件> <要添加的文件路径>, 这个就是将文件添加到打包好的apk文件中

    下面举例用代码将com.android.contacts.common和com.android.incallui一起打包到Dialer.apk

    1.在Android.mk中添加如下代码

    /home/android/xxx/android/packages/apps/Dialer/Android.mk

    LOCAL_AAPT_FLAGS := \
           --auto-add-overlay \
           --extra-packages com.android.contacts.common \
           --extra-packages com.android.incallui
    

    –extra-packages 是为资源文件设置别名:意思是通过该应用包名+R,com.android.contacts.common.R和com.android.incallui.R都可以访问到资源
    LOCAL_AAPT_FLAGS 的作用是把 –extra-packages 标签后面的包附加在要生成的APK里面

    2. 将com.android.contacts.common和com.android.incallui的AndroidManifest.xml文件中的内容移植到Dialer的AndroidManifest.xml
    3. 再用mm指令编译并生成Dialer.apk

    到此就将com.android.contacts.common和com.android.incallui一起打包到Dialer.apk中了,因此当我们替换InCallUI.apk时不会起作用

    检查overlay是否生效

    用反编译的方式来检查是否生效,aapt既可以打包apk中资源文件,也可以反编译出apk中资源文件内容

    1.使用AAPT检测

    aapt dump 命令

    aapt d[ump] [—values] WHAT file.{apk} [asset[asset …]]
    

    badging-----------Print the label and icon for the app declared in APK
    permissions------Print the permissions from the APK
    resources---------Print the resource table from the APK
    configurations----Print the configurations in the APK
    xmltree-------------Print the compiled xmls in the given assets
    xmlstrings---------Print the strings of the given compiled xml assets

    aapt d badging xxx.apk :显示标签、图标和应用程序的相关描述 aapt d permissions xxx.apk
    :显示apk所具有的系统权限 aapt d resources xxx.apk :查看apk资源 aapt d configurations
    xxx.apk :查看apk配置 aapt d xmltree facebook_googleplay_V1.0.0.apk
    res/anim-v21/design_bottom_sheet_slide_out.xml
    :查看design_bottom_sheet_slide_out.xml的树形结构 aapt d xmlstrings
    facebook_googleplay_V1.0.0.apk
    res/anim-v21/design_bottom_sheet_slide_out.xml :查看xml中所有的string

    2.使用apktools检测

    反编译工具

    二.预制apk

    带源码的预制

    在订单目录下内置SetupWizard
    /home/android/xxx/android/vendor/xxx/trunk/configs.mk

    $(call inherit-product-if-exists, $(LOCAL_PATH)/packages/apps/apps.mk)
    

    在/home/android/xxx/android/vendor/tinno/xxx/trunk/packages/apps/目录
    下新建SetupWizard目录,并把源码放在放在新建的SetupWizard目录下
    /home/android/xxx/android/vendor/xxx/trunk/packages/apps/apps.mk

    LOCAL_PATH := vendor/tinno/$(TARGET_PRODUCT)/$(PROJECT_NAME)/packages/apps/
    
    PRODUCT_PACKAGES += \
        SetupWizard \
    

    重写编写Android.mk文件

    LOCAL_PATH:= $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_PACKAGE_NAME := SetupWizard
    LOCAL_PRIVATE_PLATFORM_APIS := true
    LOCAL_PRIVILEGED_MODULE := true
    LOCAL_MODULE_TAGS := optional
    LOCAL_CERTIFICATE := platform
    #LOCAL_OVERRIDES_PACKAGES := Provision
    LOCAL_PROGUARD_ENABLED := disabled
    
    LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
    LOCAL_SRC_FILES := $(call all-java-files-under)
    LOCAL_JAVA_LIBRARIES += telephony-common
    LOCAL_USE_AAPT2 := true
    
    LOCAL_STATIC_ANDROID_LIBRARIES := setup-wizard-lib
    
    include frameworks/opt/setupwizard/library/common-gingerbread.mk
    include $(BUILD_PACKAGE)
    include $(call all-makefiles-under,$(LOCAL_PATH))
    

    重新build项目即可

    不带源码的预制

    前面步骤一样
    Android.mk文件

    LOCAL_PATH:= $(call my-dir)
    #system
    ###################################################################
    include $(CLEAR_VARS)
    LOCAL_MODULE := LegalInformation
    LOCAL_MODULE_CLASS := APPS
    LOCAL_MODULE_TAGS := optional
    LOCAL_BUILT_MODULE_STEM := package.apk
    LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
    LOCAL_CERTIFICATE := platform
    LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
    include $(BUILD_PREBUILT)
    ###################################################################
    

    Android.mk文件解释
    Android.mk语法规范
    Android.mk解析

    展开全文
  • Android overlay使用

    2020-10-12 14:14:05
    二、 RRO–Runtime resource overlay(运行时替换) 2.1 资源相关知识点 2.2编写RRO apk 2.3 RRO apk的安装 一、SRO–Static resource overly(静态替换) SRO是在编译时完成的,为app或者framework加载不同的资源...

    目录

    一、SRO–Static resource overly(静态替换)

    1.1 添加路径,配置资源

    1.2 编写mk文件

    二、 RRO–Runtime resource overlay(运行时替换)

    2.1 资源相关知识点

    2.2编写RRO apk

    2.3 RRO apk的安装


    一、SRO–Static resource overly(静态替换)

    SRO是在编译时完成的,为app或者framework加载不同的资源,这样可以针对不同项目使用不同的资源

    1.1 添加路径,配置资源

        编写overlay文件时注意一下:

    •         对于可以根据key区分的资源,我们的overlay文件名字可以不和源包里的文件名字一致;
             [例如]string, string, array, bool, style,dimen等。
    •         对于依靠文件名字区分的资源, 我们的overlay文件名字必须和源包里的文件名字一致;
              [例如]drawable,layout,menu,animation,raw等。

    1.2 编写mk文件

        将路径添加到PRODUCT_PACKAGE_OVERLAYS或DEVICE_PACKAGE_OVERLAYS变量中。
        PRODUCT_PACKAGE_OVERLAYS:用于一个指定的产品,即某个品牌的某个型号。
        DEVICE_PACKAGE_OVERLAYS: 用于某个品牌的所有产品。
        一般"device/" 路径下的overlay使用DEVICE_PACKAGE_OVERLAYS,而"vendor/"路径下的overlay使用    PRODUCT_PACKAGE_OVERLAYS。
       如果我们在编译时PRODUCT_PACKAGE_OVERLAYS和DEVICE_PACKAGE_OVERLAYS都被加载而且overlay了源包中的相 同的资源,那么PRODUCT_PACKAGE_OVERLAYS中的overlay资源会被最终使用。

    DEVICE_PACKAGE_OVERLAYS += think/overlay

    二、 RRO–Runtime resource overlay(运行时替换)

    2.1 资源相关知识点

    我们可以将Android资源类型分为三类

    1. 列表类型的xml资源文件,该类型的文件内部包含了多个资源项,典型的就是strings.xml,很多项目会有config.xml也大都是这种类型的文件。
    2. 值类型的xml文件,该类型的一个文件对应了一个资源项,比如layout目录下的布局文件,以及某些项目menu目录下的xml文件也大都是这种类型。
    3. 数据型资源文件,比如图片,音频,视频以及其他数据类型的文件,该类型的一个文件对应一个资源项。

    2.2编写RRO apk

    RRO apk中不包含code,只有资源配置。AndroidMenifest.xml文件是重点。 

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.zhuang.demo">
        <overlay android:targetPackage="com.zhuang.demo" android:priority="1"/>
    </manifest>

    “android:targetPackage"是我们要替换资源的目标apk的包名,如果要替换framework下的资源,可以将"android:targetPackage"写成"android”。

    2.3 RRO apk的安装

    由于RRO 可以修改资源,所以出于安全方面的考虑,只有"vendor/overlay"和“product/overlay”目录下的overlay apk才被接收(system/vendor/overlay和vendor/overlay是相关联的; 基于Android P)。资源ID是在apk编译过程中生成的,而overlay apk的编译时不用目标apk, 所以overlay apk中的资源并不会和目标apk中的资源分配同样的ID。这样资源的匹配只能通过我们定义的字符名称来完成,而字符匹配比较慢。在apk安装的过程中,“idmap"工具会帮忙生成map, 用来帮助overlay apk和目标apk同步资源。这个map数据存储在手机的”/data/resource-cache"目录中

    展开全文
  • 悬浮窗权限:悬浮窗权限是Android系统中非常高级的权限,可以实现如下功能1、app外弹框2、app外开启其他权限引导3、app外护眼模式开发4、配合ACC权限实现系统设置5、app外视频悬浮等一、悬浮窗兼容方案1、如果有权限...

    悬浮窗权限:

    悬浮窗权限是Android系统中非常高级的权限,可以实现如下功能

    1、app外弹框

    2、app外开启其他权限引导

    3、app外护眼模式开发

    4、配合ACC权限实现系统设置

    5、app外视频悬浮等

    一、悬浮窗兼容方案

    1、如果有权限,使用type_overlay或者type_phone

    2,如果没有权限使用type_toast方案

    二、代码实现

    权限注册

    悬浮窗管理

    public class OverlayWindowManager {

    private static final String TAG = "OverlayWindowManager";

    private static boolean DEBUG = BuildConfig.DEBUG;

    private static Object getLock() {

    WindowManagerGlobal managerGlobal;

    managerGlobal = WindowManagerGlobal.getInstance();

    try {

    return new Hack(WindowManagerGlobal.class, "mLock").getField(managerGlobal);

    }

    catch (Exception e) {

    e.printStackTrace();

    }

    return null;

    }

    /**

    * 当前是否可显示Toast

    * @return

    */

    public static boolean canShowToast() {

    if (Build.VERSION.SDK_INT < 25) {

    return true;

    }

    WindowManagerGlobal managerGlobal = WindowManagerGlobal.getInstance();

    try {

    synchronized (getLock()) {

    ArrayList mParams = new Hack>(WindowManagerGlobal.class, "mParams").getField(managerGlobal);

    if (null != mParams) {

    int size = mParams.size();

    for (int i = size - 1; i >= 0 && i < size; i--) {

    if (i < mParams.size()) {

    WindowManager.LayoutParams p = mParams.get(i);

    if (null == p) {

    continue;

    }

    if (p.type == WindowManager.LayoutParams.TYPE_TOAST) {

    return false;

    }

    }

    }

    }

    }

    }

    catch (Exception e) {

    if (DEBUG) {

    e.printStackTrace();

    L(e.getMessage());

    }

    else {

    throwException(e);

    }

    }

    return true;

    }

    /**

    * 暂不提供Remove方法 因为该方法remove后 功能程序调用remove

    * 后 findViewLocked 会抛出 IllegalArgumentException异常

    */

    private static void forceRemoveAll() {

    // WindowManagerGlobal managerGlobal = WindowManagerGlobal.getInstance();

    // try {

    // synchronized (getLock()) {

    // ArrayList mParams = new Hack>(WindowManagerGlobal.class, "mParams").getField(managerGlobal);

    // ArrayList views = new Hack>(WindowManagerGlobal.class, "mViews").getField(managerGlobal);

    // if (null != mParams && null != views) {

    // int size = mParams.size();

    // for (int i = size - 1; i >= 0 && i < size; i--) {

    // if (i < mParams.size()) {

    // WindowManager.LayoutParams p = mParams.get(i);

    // if (null == p) {

    // continue;

    // }

    // if (p.type == WindowManager.LayoutParams.TYPE_TOAST) {

    // int index = mParams.indexOf(p);

    // if (index < views.size() && index >= 0) {

    // View view = views.get(index);

    // managerGlobal.dismiss(view, true);

    // }

    // }

    // }

    // }

    // }

    // }

    // }

    // catch (Exception e) {

    // if (DEBUG) {

    // L(e.getMessage());

    // }

    // else {ViewManager

    // throwException(e);

    // }

    // }

    }

    public static void showWindow(WindowManager windowManager, View view, WindowManager.LayoutParams layoutParams) {

    showWindow(true, windowManager, view, layoutParams);

    }

    /**

    *

    * @param isForceShow 是否强制显示 为false不会显示 强制显示会强制关闭其他Type_Toast类型的Window

    * @param windowManager

    * @param view

    * @param layoutParams

    *

    */

    public static void showWindow(boolean isForceShow, WindowManager windowManager, View view, WindowManager.LayoutParams layoutParams) {

    if (windowManager == null || view == null || layoutParams == null) {

    return;

    }

    if (isWindowOverlayGranted(view.getContext())){ // 有悬浮权限

    if(Build.VERSION.SDK_INT >Build.VERSION_CODES.P){

    layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY - 1;

    } else if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.O){

    layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

    }else{

    layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

    }

    addViewToWindow(windowManager, view, layoutParams);

    return;

    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ //8.0

    layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY -1;

    addViewToWindow(windowManager, view, layoutParams);

    return;

    }

    if(!isForceShow){

    return;

    }

    Context context = view.getContext();

    if(!(context instanceof OverlayContextWrapper)){

    throw new ClassCastException("context can't be cast OverlayContextWrapper");

    }

    if( Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1){

    //android 7.0 无法使用type_toast,需要从NotificationManagerService中得到windowtoken才行,因此只能通过如下方式

    Toast t = new Toast(view.getContext());

    t.setView(view);

    t.setDuration(Toast.LENGTH_LONG);

    showToast(t);

    return;

    }

    }

    public static boolean isWindowOverlayGranted(Context context) {

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){

    return Settings.canDrawOverlays(context);

    }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

    return checkOpOverlays(context);

    }

    return hasSystemAlertWindowPermssion(context);

    }

    private static boolean hasSystemAlertWindowPermssion(Context context) {

    String packname = context.getPackageName();

    PackageManager pm = context.getPackageManager();

    boolean permission = (PackageManager.PERMISSION_GRANTED == pm.checkPermission("android.permission.SYSTEM_ALERT_WINDOW", packname));

    return permission;

    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)

    public static boolean checkOpOverlays(Context context) {

    try {

    AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);

    if (appOpsMgr == null)

    return false;

    int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), context

    .getPackageName());

    return mode == AppOpsManager.MODE_ALLOWED;

    }catch (Exception e){

    e.printStackTrace();

    }

    return false;

    }

    private static void addViewToWindow(WindowManager windowManager, View view, WindowManager.LayoutParams layoutParams) {

    if (canAddToWindowManager(view)) {

    try {

    windowManager.addView(view, layoutParams);

    } catch (Throwable t) {

    String error = String.format("OverlayWindowManager addViewToWindow the error message is %s ", layoutParams.type);

    SmartCrashHandler.getInstance().throwOne(new Exception(error), false);

    }

    }

    }

    private static boolean canAddToWindowManager(View view) {

    return view != null && view.getParent() == null;

    }

    public static void showToast(Toast toast) {

    showToast(toast, false);

    }

    /**

    *

    * @param toast

    * @param isForceShow 是否强制显示 为false不会显示 强制显示会强制关闭其他Type_Toast类型的Window

    */

    public static void showToast(final Toast toast, boolean isForceShow) {

    if (toast == null) {

    return;

    }

    if (canShowToast()) {

    toast.show();

    return;

    }

    if (isForceShow) {

    forceRemoveAll();

    toast.show();

    }

    }

    public static void throwException(Exception e) {

    // 捕获异常到crash平台并继续崩溃

    SmartCrashHandler.getInstance().throwOne(e, true);

    }

    private static class Hack {

    private Field mField;

    Hack(Class cls, String name) throws Exception {

    Field field = null;

    if (cls == null) {

    this.mField = null;

    return;

    }

    try {

    field = cls.getDeclaredField(name);

    field.setAccessible(true);

    } catch (Exception e) {

    L(e.getMessage());

    throw e;

    } finally {

    this.mField = field;

    }

    }

    @SuppressWarnings("unchecked")

    public T getField(C c) throws IllegalAccessException {

    return (T) this.mField.get(c);

    }

    }

    private static void L(String msg) {

    if (DEBUG) {

    Log.d(TAG, msg);

    }

    }

    }

    三、TYPE_TOAST兼容

    android 7.0之后无法实现type_toast,需要通过NotificationManagerService获取到一个WindowToken才行,因此我们使用Toast来实现,通过Hook方式替换布局参数,即可做到兼容

    public void handleShow() {

    if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView

    + " mNextView=" + mNextView);

    if (mView != mNextView) {

    // remove the old view if necessary

    handleHide();

    mView = mNextView;

    Context context = mView.getContext().getApplicationContext();

    String packageName = mView.getContext().getOpPackageName();

    if (context == null) {

    context = mView.getContext();

    }

    mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);

    // We can resolve the Gravity here by using the Locale for getting

    // the layout direction

    final Configuration config = mView.getContext().getResources().getConfiguration();

    final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());

    mParams.gravity = gravity;

    if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {

    mParams.horizontalWeight = 1.0f;

    }

    if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {

    mParams.verticalWeight = 1.0f;

    }

    mParams.x = mX;

    mParams.y = mY;

    mParams.verticalMargin = mVerticalMargin;

    mParams.horizontalMargin = mHorizontalMargin;

    mParams.packageName = packageName;

    if (mView.getParent() != null) {

    if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);

    mWM.removeView(mView);

    }

    if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);

    mWM.addView(mView, mParams);

    trySendAccessibilityEvent();

    }

    }

    因为这里我们定义WindowManagerHook

    public class WindowManagerHook implements WindowManager {

    WindowManager mWm;

    WindowManager.LayoutParams mLayoutParams;

    private WindowManagerHook(WindowManager windowManager, WindowManager.LayoutParams layoutParams) {

    this.mWm = windowManager;

    this.mLayoutParams = layoutParams;

    }

    public static WindowManager hook(WindowManager mWm, WindowManager.LayoutParams layoutParams){

    return new WindowManagerHook(mWm,layoutParams);

    }

    @Override

    public Display getDefaultDisplay() {

    if(mWm==null) return null;

    return mWm.getDefaultDisplay();

    }

    @Override

    public void removeViewImmediate(View view) {

    if(mWm==null) return ;

    mWm.removeViewImmediate(view);

    }

    @Override

    public void addView(View view, ViewGroup.LayoutParams params) {

    if(mWm==null) return;

    if((params instanceof WindowManager.LayoutParams)){

    WindowManager.LayoutParams originLayoutParams = (LayoutParams) params;

    if(originLayoutParams.type== LayoutParams.TYPE_TOAST){ //如果是typeToast,直接替换params

    originLayoutParams.flags = this.mLayoutParams.flags;

    originLayoutParams.gravity = this.mLayoutParams.gravity;

    originLayoutParams.alpha = this.mLayoutParams.alpha;

    originLayoutParams.dimAmount = this.mLayoutParams.dimAmount;

    originLayoutParams.windowAnimations = this.mLayoutParams.windowAnimations;

    originLayoutParams.format = this.mLayoutParams.format;

    originLayoutParams.screenBrightness = this.mLayoutParams.screenBrightness;

    originLayoutParams.screenOrientation = this.mLayoutParams.screenOrientation;

    originLayoutParams.verticalMargin = this.mLayoutParams.verticalMargin;

    originLayoutParams.horizontalMargin = this.mLayoutParams.horizontalMargin;

    originLayoutParams.softInputMode = this.mLayoutParams.softInputMode;

    originLayoutParams.verticalWeight = this.mLayoutParams.verticalWeight;

    }

    }

    mWm.addView(view,params);

    }

    @Override

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

    if(mWm==null) return ;

    mWm.updateViewLayout(view,params);

    }

    @Override

    public void removeView(View view) {

    if(mWm==null) return ;

    mWm.removeView(view);

    }

    }

    如果要注入Hook,需要通过替换Context 实现 ,因为Toast源码中handleShow方法中,windowManager通过view的Context获取

    public class OverlayContextWrapper extends ContextWrapper {

    private WindowManager.LayoutParams mLayoutParams;

    public OverlayContextWrapper(Context base,WindowManager.LayoutParams layoutParams) {

    super(base);

    this.mLayoutParams = layoutParams;

    }

    @Override

    public Context getApplicationContext() {

    return this; //替换为自己

    }

    @Override

    public Object getSystemService(String name) {

    if(!Context.WINDOW_SERVICE.equals(name)) { //主动拦截

    return super.getSystemService(name);

    }

    Context base = getBaseContext();

    return OverlayViewHelper.getSystemWindowManagerHook(base,this.mLayoutParams);

    }

    }

    工具类

    public class OverlayViewHelper {

    public static LayoutInflater getLayoutInflater(Context context,WindowManager.LayoutParams lp ){

    Context appContext = context.getApplicationContext();

    OverlayContextWrapper toastAppContext = new OverlayContextWrapper(appContext,lp);

    return LayoutInflater.from(appContext).cloneInContext(toastAppContext);

    }

    public static WindowManager getSystemWindowManagerHook(Context appContext,WindowManager.LayoutParams lp){

    WindowManager windowManager = (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE);

    WindowManager hooker = WindowManagerHook.hook(windowManager,lp);

    return hooker;

    }

    }

    四、使用方法

    public static void showWindow(Context context,int layoutId,WindowManager.LayoutParams lp){

    LayoutInflater inflater = OverlayViewHelper.getLayoutInflater(context,lp);

    View toastView = inflater.inflate(layoutId,null,false);

    WindowManager wm = (WindowManager) toastView.getContext().getSystemService(Context.WINDOW_SERVICE);

    if(wm instanceof WindowManagerHook){

    Log.d("WindowManager","context 注入成功");

    }

    OverlayWindowManager.showWindow(wm,toastView,lp);

    }

    展开全文
  • 用途: 客制化使用,在不需要更新app 的情况下修改app ... android:name="android.permission.CHANGE_OVERLAY_PACKAGES" tools:ignore="ProtectedPermissions" /> 打开Android Studio 编写overlay 的覆盖层apk,不

    用途:

    客制化使用,在不需要更新app 的情况下修改app 的文本、国际化翻译、色值color等等。


    使用

    1. 目标apk需要在AndroidManifest.xml中添加权限
    <uses-permission
        android:name="android.permission.CHANGE_OVERLAY_PACKAGES"
        tools:ignore="ProtectedPermissions" />
    
    1. 打开Android Studio 编写overlay 的覆盖层apk,不用写代码,只需要配置AndroidManifest文件和 你想要修改的values,比如字符串,颜色等。
    2. 在这里插入图片描述
     <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="目标的apk包名.overlay">
    
        <application android:hasCode="false" />
    
        <overlay
            android:isStatic="true"
            android:priority="1"
            android:targetPackage="目标的apk包名" />
    </manifest>
    

    注意使用android:isStatic=“true” 表示overlay默认生效,需要在项目app目录下的build.gradle中配置最小sdk : minSdkVersion 26

     -  android:priority="1" 表示权重,
     - 当同时存在多个同包名的overlay包时,优先取 权重数字大的
    
    1. 打包签名overlay包,特别注意需要与目标的apk使用相同的签名
    2. push 到/system/vendor/overlay 目录下(如文件夹不存在则创建),重启设备
    3. 重启后 使用命令行:
     adb shell cmd overlay list --user current  
    

    查看当前设备所有的overlay包,如果结果的前缀是 [x] 表示已生效;如果overlay包中没有配置 android:isStatic=“true” 这个属性,这时的结果的前缀就是: [ ]。 表示还不可用,使用

    adb shell cmd overlay enable 包名   
    

    这个命令即可激活overlay

    最后,官方overlay地址:

    https://source.android.google.cn/devices/architecture/rros#configuring-overlays

    展开全文
  • Android Overlay机制

    千次阅读 2018-06-25 14:25:32
    本章要介绍的运行时Overlay(RuntimeResourceOverlay)Android Runtime Resource Overlay 简介】并不冲突,只是在新的Android O版本,Google对于RR0机制的安全性进行了加强,后续有详细介绍。首先系统编译过程中...
  • android overlay机制研究

    2017-11-13 18:10:56
    Android overlay 机制允许在不修改packages中apk的情况下,来自定义 framework和package中的资源文件,实现资源的定制。来达到显示不同的UI的目的. 二.overlay编译流程分析 1.添加overlay 目录 1.1 Product ...
  • Android overlay简单总结

    千次阅读 2018-08-31 11:47:12
    本文简单总结Androidoverlay机制, 分为下面两部分: 1. SRO–Static resource overly(静态替换) 2. RRO–Runtime resource overlay (运行时替换) 1.SRO–Static resource overlay(静态替换) SRO是在编译时...
  • 通过一个例子来了解overlay app,修改字符串资源的过程 google messag 是google的应用,如果有字符串在某种语言下显示有问题,是无法通过源码来实现。就要用到overlay来实现。 通过ApkToolkit 工具来反编译message...
  • Overlay屏幕不对应实际的物理屏幕设备,即不存在与之对应的framebuffer设备;其实现思路是在实际屏幕设备的layerstack上创建一个layer,作为虚拟屏幕的显示载体。这个layer申请的显存来源是来自实际屏幕的hwc,而这...
  • 1 overlay可能支持的颜色格式/* possible overlay formats可能支持的颜色格式 */enum {OVERLAY_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888,OVERLAY_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565,OVERLAY_FORMAT_...
  • 基于Android Overlay机制的资源替换方法研究.pdf
  • 覆盖重写的资源文件在overlay文件夹下,可以通过grep命令在device目录下查找,因为可能不止一套overlay文件,真正使用的资源文件有可能在对应芯片型号的文件夹,也可能在google文件夹下,与lunch
  • Androidoverlay机制

    2021-06-12 12:35:28
    如何使用overlay完成app的客户定制?一、510平台代码customer关于overlay配置关系梳理如下common/xxx_Default_Property.mk:306PRODUCT_PACKAGE_OVERLAYS := $(xxx_TMP_CUSTOMER)/overlay $(PRODUCT_PACKAGE_OVERLAYS...
  • 一.overlay 介绍android overlay 机制允许在不修改packages中apk的情况下,来自定义 framework和package中的资源文件,实现资源的定制。来达到显示不同的ui的目的.二.overlay编译流程分析1.添加overlay 目录1.1 ...
  • android 怎么编译overlay

    2021-06-05 02:52:11
    AndroidOverlay学习1、 测试代码frameworks/base/libs/surfaceflinger/tests/overlays/overlays.cpp提供了一个简单的overlay调用流程,可惜这个测试程序有错误,在sp surface = client->createSurface(getpid(), ...
  • android overlay系统 overlay的硬件抽象层 camera系统与上层接口和硬件抽象层
  • Android Overlay是一种资源替换机制,它能在不重新打包apk的情况下,实现资源文件的替换(res目录非assert目录),Overlay又分为静态Overlay(Static Resource Overlay)与运行时Overlay(Runtime Resource Overlay),...
  • https://www.jianshu.com/p/d465289904a9
  • Android overlay 学习

    千次阅读 2012-11-04 19:19:05
    Overlay 分析 1 overlay 代码结构 Overlay 是个相对简单的模块,主要的文件有: /hardware/libhardware/include/hardware/overlay.h 主要定义了两个 struct, 定义为 data device 和 control
  • Android Overlay机制 GrayMonkey 0.5 2017.10.23 11:03* 字数 2134 阅读 5810评论 0喜欢 11 前言 Android Overlay是一种资源替换机制,它能在不重新打包apk的情况下,实现资源文件的替换(res目录非assert目录...
  • android overlay(个人总结)

    千次阅读 2012-07-03 15:44:01
    overlay 是直接操作显示设备驱动直接绘图的,和sufaceflinger 通过混色后绘制到显示设备上是有区别的, 1. overlay hal对象的获取 // layerbuffer.cpp LayerBuffer::OverlaySource::OverlaySource() ----> ...
  • Voice overlay helps you turn your user's voice into text, providing a polished UX while handling for you the necessary permission. Demo You can clone this repo, then run the Demo ...
  • Android Overlay学习 一

    2010-08-11 11:10:37
    前文仅了解了overlay HAL的架构,下面继续看看系统层是如何调用Overlay模块。 1、 测试代码 frameworks/base/libs/surfaceflinger/tests/overlays/overlays.cpp提供了一个简单的overlay调用流程,可惜这个测试程序...
  • Android O overlay framework-res resource 不想修改android原生配置文件,需要单独overlay相关修改。 android原生默认高温关机,电池温度设置为68度,不同项目,要求不一样,如下范例要求61%执行高温关机流程。 ...
  • Android overlay系统

    2015-07-21 00:18:11
    视频输出系统在android中体现为overlayoverlay的意思为 :覆盖铺在…上面,既然覆盖我们要考虑主要是覆盖在什么之上,在linux显示系统中用于界面显示的一般使用帧缓存(framebuffer),framebuffer是linux系统为显示...
  • A overlay_B DEVICE_PACKAGE_OVERLAYS = overlay_C overlay_D 上述overlay目录优先级顺序:overlay_A >overlay_B> overlay_C >overlay_D overlay用于新增资源 默认情况下,overlay目录的资源文件内容只能覆盖原有...
  • Android RRO Overlay应用范例 RRO(Runtime Resource Overlay) 在2014年,索尼把自己的主题引擎RRO(Runtime Resource Overlay) 在安卓7.0中,Google也在原生安卓内置了该引擎 Android 开发中的应用 RRO不仅针对...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,947
精华内容 8,378
关键字:

androidoverlay