当前位置: 首页 > news >正文

Android 动态设置默认Launcher(默认应用 电话-短信-浏览器-主屏幕应用))

Android 动态设置默认Launcher(默认应用 电话-短信-浏览器-主屏幕应用))

文章目录

  • 场景需求
  • 参考资料
  • 思路
    • 期待效果
  • 实现方案
  • 源码流程分析和思路实现
    • DefaultAppActivity
    • HandheldDefaultAppFragment
    • HandheldDefaultAppPreferenceFragment
    • DefaultAppChildFragment
    • DefaultAppViewModel
    • ManageRoleHolderStateLiveData
    • 小结
    • RoleManager
      • addRoleHolderAsUser
    • IRoleManager aidl 接口
    • RoleService addRoleHolderAsUser
    • RoleControllerManager
    • RoleControllerService
    • IRoleController
    • RoleControllerServiceImpl
      • addRoleHolderInternal
      • RoleManager addRoleHolderFromController
    • RoleService
      • addRoleHolderFromController
      • getOrCreateUserState
    • RoleUserState ->addRoleHolder
      • scheduleWriteFileLocked
      • writeFile
    • RolesPersistenceImpl ->writeForUser
      • getFile 方法
      • roles.xml
      • 小结
      • Launcher 指定默认流程总结
      • 指定其它默认App 方案
  • 总结
    • 扩展知识
    • adb 命令来动态设置默认的Launcher
    • adb 命令实现思考


场景需求

  • 客需项目中,客户需要有自己Launcher 形态出现
    Android系统本身有自己的Launcher,目前看到的基本上都是Launcher3,几年前还看到Launcher2. 目前接触到的原生Launcher基本都是Launcher3,客户需要有自己Launcher 形态出现

  • 客户在定制自己产品时候有自己默认Launcher,或者整个产品形态要么只有自己一个Launcher 要么客需Launcher和系统Launcher3,但是Launcher3 从不显示

  • 在已经成型的产品中,可能多个Launcher存在,客户需要在已有产品的基础上,动态切换自己默认的Launcher。 很多白牌产品,借用已有的产品形态,更换Launcher 就是一个新的产品,最重要的是实际商业中不需要备货一说

备注: 默认电话、短信、浏览器和主屏 一样的思路,代码都完全一样,参数不一样而已。

参考资料

Setting 里面切换不同Launcher

高通Android 8.1/9/10/11/12/13 通过包名设置默认launcher
android 10 设置app为默认浏览器
Android11 设置第三方Launcher并默认 与 如何预置apk

本来是分析 默认Launcher 的,实际实验发现 默认电话、短信、桌面、浏览器 是一样的思路,索性放在一起,以默认Launcher为例进行总结说明。

思路

从设置默认主屏幕地方入手,设置->Launcher应用->主屏幕应用选择-> 桌面应用切换
然后进行源码分析,实现的具体方案是怎样的。

如下,实际设置中切换的入口
在这里插入图片描述

期待效果

能在应用层实现这个效果,一个界面动态切换不同的Launcher
可以adb 命令实现这个功能
在效果一的基础上,可以考虑集成到framework层,对外提供接口,应用调用framework层接口也可以。

如下测试图,验证过,三个按钮实现切换。

在这里插入图片描述

实现方案

对 RoleManager 类的 addRoleHolderAsUser 方法,进行反射实现。

路径:/packages/modules/Permission/framework-s/java/android/app/role/RoleManager.java

实际控制方法 如下反射实现:

 val roleManager: RoleManager =
                ContextProvider.get().context.getSystemService<RoleManager>(RoleManager::class.java)
            val executor: Executor = ContextProvider.get().context.getMainExecutor()
            val callback =
                Consumer<Boolean> { successful: Boolean ->
                    if (successful) {
                        Log.i(TAG, " 成功了==> Package " + "added" + " as role holder, role: " + "测试Home 桌面" + ", package: " + "com.nd.android.pandahome2")
                    } else {
                        Log.i(TAG, " 失败了==>  Package " + "added" + " as role holder, role: " + "测试Home 桌面" + ", package: " + "com.nd.android.pandahome2")
                    }
                }
            val addRoleHolderAsUser = roleManager.javaClass.getMethod(
                "addRoleHolderAsUser",
                String::class.java, String::class.java, Int::class.java,
                UserHandle::class.java,Executor::class.java,Consumer::class.java
            )
            addRoleHolderAsUser.isAccessible = true
            val userHandle = NavigationHelper.newInstance(
                UserHandle::class.java, arrayOf<Class<*>?>(
                    Int::class.javaPrimitiveType
                ), 0
            ) as UserHandle
            addRoleHolderAsUser.invoke(roleManager, "android.app.role.HOME", "com.nd.android.pandahome2",
                1,userHandle,executor,callback)
        }

源码流程分析和思路实现

如上 思路 说明部分,以设置进入Launcher 选择为切入点,进行分析,下面主要涉及到的内和方法。

相关调用
DefaultAppActivityfragment = AutoDefaultAppFragment.newInstance(roleName, user);
HandheldDefaultAppFragmentHandheldDefaultAppPreferenceFragment.newInstance(mRoleName, mUser);
HandheldDefaultAppPreferenceFragmentDefaultAppChildFragment.newInstance(mRoleName,mUser);
DefaultAppChildFragmentonPreferenceClick ->setDefaultApp(packageName); -> mViewModel.setDefaultApp
DefaultAppViewModelsetDefaultApp
ManageRoleHolderStateLiveDatasetRoleHolderAsUser
RoleManageraddRoleHolderAsUser
IRoleManageraidl 接口功能定义 addRoleHolderAsUser
RoleServiceaddRoleHolderAsUser
RoleControllerManageronAddRoleHolder
RoleControllerServiceonAddRoleHolder
IRoleControllerIRoleController.aidl 文件
RoleControllerServiceImplonAddRoleHolder ->addRoleHolderInternal
RoleManageraddRoleHolderFromController
RoleServiceaddRoleHolderFromController
RoleUserStateaddRoleHolder->scheduleWriteFileLocked->writeFile
RolesPersistencewriteForUser
RolesPersistenceImplwriteForUser

DefaultAppActivity

路径:/packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppActivity.java 
com.google.android.permissioncontroller/com.android.permissioncontroller.role.ui.DefaultAppActivity 

从包名路径就可以看到,这是一个包,看看实际源码结构,看着是一个软件包 如下,实际发现安卓系统结构里面又没有这个包名的包。导入工程分析
结合界面,就是要分析 桌面应用选择点击动作,看看如下截图,发现它只是一个入口,啥也没有。 进入到HandheldDefaultAppFragment 看看

在这里插入图片描述

HandheldDefaultAppFragment

实际代码截图如下,也是几行代码,它也是一个入口而已
在这里插入图片描述

HandheldDefaultAppPreferenceFragment

实际代码截图如下,发现它也还是一个简单的类,入口而已

在这里插入图片描述

DefaultAppChildFragment

看代码截图,和点击事件有点像的就是 就是 onPreferenceClick 方法了。 继续分析源码

在这里插入图片描述

DefaultAppViewModel

看看类注释,就是默认APP 的功能, setDefaultApp 方法也是设置包名作为默认app 的功能,


/**
 * {@link ViewModel} for a default app.
 */
public class DefaultAppViewModel extends AndroidViewModel {
 


    /**
     * Set an application as the default app.
     *
     * @param packageName the package name of the application
     */
    public void setDefaultApp(@NonNull String packageName) {
        if (mManageRoleHolderStateLiveData.getValue() != ManageRoleHolderStateLiveData.STATE_IDLE) {
            Log.i(LOG_TAG, "Trying to set default app while another request is on-going");
            return;
        }
        mManageRoleHolderStateLiveData.setRoleHolderAsUser(mRole.getName(), packageName, true, 0,
                mUser, getApplication());
    }


在这里插入图片描述

ManageRoleHolderStateLiveData

居然是一个 LiveData, 订阅机制。 这也是为什么 更改默认之后全局通知的原因吧,看方法参数解释也是 角色、包名、flag、user 等,说明跟角色、权限类型相关的。


 /**
     * Set whether an application is a holder of a role, and update the state accordingly. Will
     * be no-op if the current state is not {@link #STATE_IDLE}.
     *
     * @param roleName the name of the role
     * @param packageName the package name of the application
     * @param add whether to add or remove the application as a role holder
     * @param flags optional behavior flags
     * @param user the user to manage the role holder for
     * @param context the {@code Context} to retrieve system services
     */
    public void setRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
            boolean add, int flags, @NonNull UserHandle user, @NonNull Context context) {
        if (getValue() != STATE_IDLE) {
            Log.e(LOG_TAG, "Already (tried) managing role holders, requested role: " + roleName
                    + ", requested package: " + packageName);
            return;
        }
        if (DEBUG) {
            Log.i(LOG_TAG, (add ? "Adding" : "Removing") + " package as role holder, role: "
                    + roleName + ", package: " + packageName);
        }
        mLastPackageName = packageName;
        mLastAdd = add;
        mLastFlags = flags;
        mLastUser = user;
        setValue(STATE_WORKING);

        RoleManager roleManager = context.getSystemService(RoleManager.class);
        Executor executor = context.getMainExecutor();
        Consumer<Boolean> callback = successful -> {
            if (successful) {
                if (DEBUG) {
                    Log.i(LOG_TAG, "Package " + (add ? "added" : "removed")
                            + " as role holder, role: " + roleName + ", package: " + packageName);
                }
                setValue(STATE_SUCCESS);
            } else {
                if (DEBUG) {
                    Log.i(LOG_TAG, "Failed to " + (add ? "add" : "remove")
                            + " package as role holder, role: " + roleName + ", package: "
                            + packageName);
                }
                setValue(STATE_FAILURE);
            }
        };
        if (add) {
            roleManager.addRoleHolderAsUser(roleName, packageName, flags, user, executor, callback);
        } else {
            roleManager.removeRoleHolderAsUser(roleName, packageName, flags, user, executor,
                    callback);
        }
    }

小结

上面通过界面到业务代码分析,发现都是设置默认App 入口,经过来4个类的入口调用 直到 ManageRoleHolderStateLiveData 类里面调用到 RoleManager 的 addRoleHolderAsUser 方法,才进入框架层面

RoleManager

路径:

/packages/modules/Permission/framework-s/java/android/app/role/RoleManager.java 

看类注释说明如下: 提供权限roles 管理的功能,对外通过getService 获取它

/**
 * This class provides information about and manages roles.
 * <p>
 * A role is a unique name within the system associated with certain privileges. The list of
..... 
 */
@SystemService(Context.ROLE_SERVICE)
public final class RoleManager {
..............
}

addRoleHolderAsUser

上面关联到的方法,看看源码说明,

  • 从方法注释上看就是权限添加的功能,如果已经存在 权限,那么添加后会被替换replaced
  • 看到注释上面,这个方法是一个隐藏的方法了

    /**
     * Add a specific application to the holders of a role. If the role is exclusive, the previous
     * holder will be replaced.
     * <p>
     * <strong>Note:</strong> Using this API requires holding
     * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
     * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
     *
     * @param roleName the name of the role to add the role holder for
     * @param packageName the package name of the application to add to the role holders
     * @param flags optional behavior flags
     * @param user the user to add the role holder for
     * @param executor the {@code Executor} to run the callback on.
     * @param callback the callback for whether this call is successful
     *
     * @see #getRoleHoldersAsUser(String, UserHandle)
     * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
     * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
    @SystemApi
    public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
            @ManageHoldersFlags int flags, @NonNull UserHandle user,
            @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
       ........
        try {
            mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),
                    createRemoteCallback(executor, callback));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

关注mService 是什么,看当前类定义地方,


  @NonNull
    private final IRoleManager mService;
	
	
    /**
     * Create a new instance of this class.
     *
     * @param context the {@link Context}
     * @param service the {@link IRoleManager} service
     *
     * @hide
     */
    public RoleManager(@NonNull Context context, @NonNull IRoleManager service) {
        mContext = context;
        mService = service;
    }

在这里插入图片描述

IRoleManager aidl 接口

IRoleManager 源码路径和源码如下:它只是一个接口定义,一般情况下 这个aidl 在一个服务里面引用,服务来实现 aidl 接口,实现对应的功能。
这里通过 grep 来看看 ,找到了

packages/modules/Permission/service/java/com/android/role/RoleService.java
packages/modules/Permission/service/java/com/android/role/RoleShellCommand.java

在这里插入图片描述

这里先关注 RoleService

RoleService addRoleHolderAsUser

直接看相关代码

 private class Stub extends IRoleManager.Stub {

          ....................
        @NonNull
        @Override
        public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
            enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser");
        ..........
        }

        @Override
        public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
                @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
                @NonNull RemoteCallback callback) {
            ..............

            getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags,
                    callback);
        }

   ...........
   }
   
   
   @NonNull
    private RoleControllerManager getOrCreateController(@UserIdInt int userId) {
        synchronized (mLock) {
            RoleControllerManager controller = mControllers.get(userId);
            if (controller == null) {
                Context systemContext = getContext();
                Context context;
                try {
                    context = systemContext.createPackageContextAsUser(
                            systemContext.getPackageName(), 0, UserHandle.of(userId));
                } catch (PackageManager.NameNotFoundException e) {
                    throw new RuntimeException(e);
                }
                controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName(
                        ForegroundThread.getHandler(), context);
                mControllers.put(userId, controller);
            }
            return controller;
        }
    }

上面两个方法可以理解为 :
RoleService 类的方法 addRoleHolderAsUser 实际上执行的是 RoleControllerManager 的对象调用了 onAddRoleHolder 方法

RoleControllerManager

路径:/packages/modules/Permission/framework-s/java/android/app/role/RoleControllerManager.java 

直接看源码,方法注释让我们关注 RoleControllerService 的 onAddRoleHolder 方法

 /**
     * @see RoleControllerService#onAddRoleHolder(String, String, int)
     *
     * @hide
     */
    public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
            @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.onAddRoleHolder(roleName, packageName, flags,
                    new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "onAddRoleHolder", callback);
    }

RoleControllerService

承接上文,这里看 onAddRoleHolder 方法源码

先看类注释说明

@Deprecated
@SystemApi
public abstract class RoleControllerService extends Service {

再看方法说明及注释

    private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
            @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
        boolean successful = onAddRoleHolder(roleName, packageName, flags);
        callback.sendResult(successful ? Bundle.EMPTY : null);
    }


    /**
     * Add a specific application to the holders of a role. If the role is exclusive, the previous
     * holder will be replaced.
     * <p>
     * Implementation should enforce the role requirements and grant or revoke the relevant
     * privileges of roles.
     *
     * @param roleName the name of the role to add the role holder for
     * @param packageName the package name of the application to add to the role holders
     * @param flags optional behavior flags
     *
     * @return whether this call was successful
     *
     * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor,
     *      RemoteCallback)
     */
    @WorkerThread
    public abstract boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
            @RoleManager.ManageHoldersFlags int flags);


这里发现 RoleControllerService 是个服务,但也是一个抽象的类。 最终共调用到 onAddRoleHolder,需要子类来实现的。

回归到 RoleControllerManager 源码分析,mRemoteService 调用,其实是集合 mRemoteService 里面获取的。

private final ServiceConnector<IRoleController> mRemoteService;

那也就是 要看看 IRoleController 是什么,发现图也是一个aidl 接口。

IRoleController

路径 :

packages/modules/Permission/framework-s/java/android/app/role/IRoleController.aidl

源码如下:
在这里插入图片描述
但是 搜索发现一个问题,如下截图看看:你会发现实现类其实就是上面分析的RoleControllerService , 再进一步找到实现类 RoleControllerServiceImpl,那就看实现类里面的oAddRoleHolder 方法
在这里插入图片描述

RoleControllerServiceImpl

看一下 onAddRoleHolder 方法

  @Override
    @WorkerThread
    public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
            int flags) {
     。。。。。。。。。。。。。。。

        Role role = Roles.get(this).get(roleName);
     。。。。。。。。。。。。。。。。。。

      boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
        added = addRoleHolderInternal(role, packageName, dontKillApp,
                role.shouldOverrideUserWhenGranting(), added);
        if (!added) {
            return false;
        }
        role.onHolderAddedAsUser(packageName, Process.myUserHandle(), this);
        role.onHolderChangedAsUser(Process.myUserHandle(), this);

        return true;
    }

关注下 addRoleHolderInternal方法

addRoleHolderInternal

代码如下 ,

 @WorkerThread
    private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
            boolean overrideUserSetAndFixedPermissions) {
        return addRoleHolderInternal(role, packageName, false, overrideUserSetAndFixedPermissions,
                false);
    }

    @WorkerThread
    private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
            boolean dontKillApp, boolean overrideUserSetAndFixedPermissions, boolean added) {
        role.grant(packageName, dontKillApp, overrideUserSetAndFixedPermissions, this);

        String roleName = role.getName();
        if (!added) {
            added = mRoleManager.addRoleHolderFromController(roleName, packageName);
        }
        if (!added) {
            Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName
                    + ", role: " + roleName);
        }
        return added;
    }

继续跟踪代码:

 mRoleManager.addRoleHolderFromController(roleName, packageName);

RoleManager addRoleHolderFromController

这里直接贴出源码

 /**
     * Add a specific application to the holders of a role, only modifying records inside
     * {@link RoleManager}. Should only be called from
     * {@link android.app.role.RoleControllerService}.
     * <p>
     * <strong>Note:</strong> Using this API requires holding
     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
     *
     * @param roleName the name of the role to add the role holder for
     * @param packageName the package name of the application to add to the role holders
     *
     * @return whether the operation was successful, and will also be {@code true} if a matching
     *         role holder is already found.
     *
     * @see #getRoleHolders(String)
     * @see #removeRoleHolderFromController(String, String)
     *
     * @deprecated This is only usable by the role controller service, which is an internal
     *             implementation detail inside role.
     *
     * @hide
     */
    @Deprecated
    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
    @SystemApi
    public boolean addRoleHolderFromController(@NonNull String roleName,
            @NonNull String packageName) {
        Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
        Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
        try {
            return mService.addRoleHolderFromController(roleName, packageName);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

这里又要开始追踪 mService 了 。

看构造方法:

  /**
     * Create a new instance of this class.
     *
     * @param context the {@link Context}
     * @param service the {@link IRoleManager} service
     *
     * @hide
     */
    public RoleManager(@NonNull Context context, @NonNull IRoleManager service) {
        mContext = context;
        mService = service;
    }

追踪 mService 定义:

    @NonNull
    private final IRoleManager mService;

上面再追踪 IRoleManager 时候,已经分析了这是一个aidl 接口调用,最终实现是 RoleService。

所以上面的 mService.addRoleHolderFromController(roleName, packageName); 最总到

RoleService -> addRoleHolderFromController

RoleService

addRoleHolderFromController

源码如下:

  @Override
        public boolean addRoleHolderFromController(@NonNull String roleName,
                @NonNull String packageName) {
            getContext().enforceCallingOrSelfPermission(
                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                    "addRoleHolderFromController");

            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
            Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");

            int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
            return getOrCreateUserState(userId).addRoleHolder(roleName, packageName);
        }

getOrCreateUserState

   private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
        synchronized (mLock) {
            RoleUserState userState = mUserStates.get(userId);
            if (userState == null) {
                userState = new RoleUserState(userId, mPlatformHelper, this);
                mUserStates.put(userId, userState);
            }
            return userState;
        }
    }

这个方法返回的实例是什么,RoleUserState ,那不就是调用功能 RoleUserState 的 addRoleHolder 方法了嘛

RoleUserState ->addRoleHolder

    /**
     * Add a holder to a role.
     *
     * @param roleName the name of the role to add the holder to
     * @param packageName the package name of the new holder
     *
     * @return {@code false} if and only if the role is not found
     */
    @CheckResult
    public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
        boolean changed;

        synchronized (mLock) {
        ......
            changed = roleHolders.add(packageName);
            if (changed) {
                scheduleWriteFileLocked();
            }
        }
        if (changed) {
            mCallback.onRoleHoldersChanged(roleName, mUserId);
        }
        return true;
    }

scheduleWriteFileLocked

 /**
     * Schedule writing the state to file.
     */
    @GuardedBy("mLock")
    private void scheduleWriteFileLocked() {
        if (mDestroyed) {
            return;
        }

        if (!mWriteScheduled) {
            mWriteHandler.postDelayed(this::writeFile, WRITE_DELAY_MILLIS);
            mWriteScheduled = true;
        }
    }

writeFile

 @WorkerThread
    private void writeFile() {
        RolesState roles;
        synchronized (mLock) {
            if (mDestroyed) {
                return;
            }
            mWriteScheduled = false;
            roles = new RolesState(mVersion, mPackagesHash,
                    (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
        }
        mPersistence.writeForUser(roles, UserHandle.of(mUserId));
    }

继续跟踪 mPersistence,如下定义:

    private final RolesPersistence mPersistence = RolesPersistence.createInstance();

如下 看RolesPersistence 源码,是一个接口,那么就找实现类:RolesPersistenceImpl
在这里插入图片描述

RolesPersistenceImpl ->writeForUser

终于到头了,这里看到XML 解析并写文件
在这里插入图片描述

getFile 方法

  @NonNull
    private static File getFile(@NonNull UserHandle user) {
        ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
        File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
        return new File(dataDirectory, ROLES_FILE_NAME);
    }

看看 这个类的参数定义:

public class RolesPersistenceImpl implements RolesPersistence {

    private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName();

    private static final String APEX_MODULE_NAME = "com.android.permission";

    private static final String ROLES_FILE_NAME = "roles.xml";

那么我继续看看 roles.xml 是什么

roles.xml

位置

./vendor/mediatek/proprietary/packages/apps/PermissionController/res/xml/roles.xml
./vendor/mediatek/proprietary/packages/apps/PermissionController/tests/mocking/main_res/xml/roles.xml

这里举例HOME 相关的Launcher 部分,方便分析:

   <role
        name="android.app.role.HOME"
        behavior="HomeRoleBehavior"
        description="@string/role_home_description"
        exclusive="true"
        label="@string/role_home_label"
        overrideUserWhenGranting="true"
        requestDescription="@string/role_home_request_description"
        requestTitle="@string/role_home_request_title"
        searchKeywords="@string/role_home_search_keywords"
        shortLabel="@string/role_home_short_label">
        <!-- Also used by HomeRoleBehavior.getFallbackHolder(). -->
        <required-components>
            <activity>
                <intent-filter>
                    <action name="android.intent.action.MAIN" />
                    <category name="android.intent.category.HOME" />
                </intent-filter>
            </activity>
        </required-components>
        <preferred-activities>
            <preferred-activity>
                <activity>
                    <intent-filter>
                        <action name="android.intent.action.MAIN" />
                        <category name="android.intent.category.HOME" />
                    </intent-filter>
                </activity>
                <intent-filter>
                    <action name="android.intent.action.MAIN" />
                    <category name="android.intent.category.HOME" />
                </intent-filter>
            </preferred-activity>
        </preferred-activities>
    </role>

看name 属性,不就是 如上分析RoleManager 里面 如下方法的role 参数值嘛:

 roleManager.addRoleHolderAsUser(roleName, packageName, flags, user, executor, callback);

小结

Launcher 指定默认流程总结

所以,整套流程分析下来,设置默认App,这里HomeLauncher 举例,核心逻辑就是给包名一个role 权限,然后写入到文件,最后同步一次,通知系统。

指定其它默认App 方案

通过Launcher 的举例,默认其它App 作为默认的逻辑,比如默认浏览器、打电话、短信… 等,看一下 roles.xml 文件中定义的role name 是什么。 最终实现方案如上通过反射传递 packageName 和 role Name 不就可以了嘛。

总结

  • 如上分析了一整套默认Launcher 的代码逻辑业务,同理对于默认浏览器、短信、打电话等完全适用。
  • 通过反射实现 想要的功能,反射RoleManager 类,的addRoleHolderAsUser 方法比较合适,而且有回调。 整个流程里面发现其它类的调用并不合适

扩展知识

上面 动态实现了自己想要的功能,可以集成到framework层的服务里面对外提供接口来用,也可以最简单方式 让应用端直接反射实现。 那么有木有可能adb 命令来实现上面的需求呢?

adb 命令来动态设置默认的Launcher

实现方案如下:

 pm set-home-activity   com.zeasn.whale.saas.touch[需要设置Launcher的包名]

adb 命令实现思考

adb 为什么可以实现? 我们在上面分析了几个aidl 接口串来串去,其实adb 命令系统提供了支持。
如下截图 部分源码分享:
在这里插入图片描述

 private int runSetHomeActivity() {
        final PrintWriter pw = getOutPrintWriter();
        int userId = UserHandle.USER_SYSTEM;
        String opt;
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "--user":
                    userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                default:
                    pw.println("Error: Unknown option: " + opt);
                    return 1;
            }
        }

        String pkgName;
        String component = getNextArg();
        if (component.indexOf('/') < 0) {
            // No component specified, so assume it's just a package name.
            pkgName = component;
        } else {
            ComponentName componentName =
                    component != null ? ComponentName.unflattenFromString(component) : null;
            if (componentName == null) {
                pw.println("Error: invalid component name");
                return 1;
            }
            pkgName = componentName.getPackageName();
        }
        final int translatedUserId =
                translateUserId(userId, UserHandle.USER_NULL, "runSetHomeActivity");
        final CompletableFuture<Boolean> future = new CompletableFuture<>();
        try {
            RoleManager roleManager = mContext.getSystemService(RoleManager.class);
            roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, pkgName, 0,
                    UserHandle.of(translatedUserId), FgThread.getExecutor(), future::complete);
            boolean success = future.get();
            if (success) {
                pw.println("Success");
                return 0;
            } else {
                pw.println("Error: Failed to set default home.");
                return 1;
            }
        } catch (Exception e) {
            pw.println(e.toString());
            return 1;
        }
    }

其实最终调用的也是 RoleManager 的 addRoleHolderAsUser 方法来实现的。

相关文章:

  • MCP Server 实现一个 天气查询
  • 【C语言】分支与循环(上)
  • linux打包前端vue,后端springboot项目
  • CentOS 7 挂载与卸载文件系统笔记
  • 物联网中的物模型是什么意思,在嵌入式软件开发中如何体现?
  • 树——构造和遍历问题——面试专题
  • 屏幕后处理Post-Processing安装及使用
  • 如何学习并使用C++
  • Linux中的信号
  • Table ‘spzx-system.QRTZ_LOCKS‘ doesn‘t exist
  • 安全+低碳+高效:Acrel-3000助力企业打造未来型电能管理体系-安科瑞黄安南
  • 国内AI与国际AI的差距分析
  • 介绍FRAMES:一个统一的检索增强生成评估框架
  • Python入门学习笔记 - 从环境搭建到基础语法
  • 【Java SE】包装类 Byte、Short、Integer、Long、Character、Float、Double、Boolean
  • JDBC删除与查询
  • PLC协议
  • 动态代理模式实现与对比(JDK、CGLIB、Spring AOP)
  • vue数据两个相同的参数对比只显示一个
  • HarmonyOS主题管理工具封装:动态切换、持久化存储与常见问题解析
  • 招商蛇口:今年前4个月销售额约498.34亿元
  • 外交部:习近平主席同普京总统达成许多新的重要共识
  • 上海发布预付卡消费“10点提示”:警惕“甩锅闭店”套路
  • 见微知沪|优化营商环境,上海为何要当“细节控”自我加压?
  • 复旦设立新文科发展基金,校友曹国伟、王长田联合捐赠1亿助力人文学科与社会科学创新
  • 趣看 | 五一黄金周:你拍风景,拍风景的人在拍你