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 = trueval userHandle = NavigationHelper.newInstance(UserHandle::class.java, arrayOf<Class<*>?>(Int::class.javaPrimitiveType), 0) as UserHandleaddRoleHolderAsUser.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)@SystemApipublic 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 是什么,看当前类定义地方,

@NonNullprivate 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@Overridepublic List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser");..........}@Overridepublic 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);}...........}@NonNullprivate 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)*/@WorkerThreadpublic 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@WorkerThreadpublic 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

代码如下 ,

 @WorkerThreadprivate boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,boolean overrideUserSetAndFixedPermissions) {return addRoleHolderInternal(role, packageName, false, overrideUserSetAndFixedPermissions,false);}@WorkerThreadprivate 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)@SystemApipublic 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 定义:

    @NonNullprivate final IRoleManager mService;

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

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

RoleService -> addRoleHolderFromController

RoleService

addRoleHolderFromController

源码如下:

  @Overridepublic 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*/@CheckResultpublic 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

 @WorkerThreadprivate 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 方法

  @NonNullprivate 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 部分,方便分析:

   <rolename="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 方法来实现的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75018.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt下载模板到本地文件内容丢失问题

上源码 关键点已标注在源码中 A, B… // 保存的文件路径后缀QString dateTime Myapp::getCurrentTimeDescYMDHms().replace(" ", "").replace("-", "").replace(":", "");// 临时文件名称QString newFileName Q…

【数学建模】动态规划算法(Dynamic Programming,简称DP)详解与应用

动态规划算法详解与应用 文章目录 动态规划算法详解与应用引言动态规划的基本概念动态规划的设计步骤经典动态规划问题1. 斐波那契数列2. 背包问题3. 最长公共子序列(LCS) 动态规划的优化技巧动态规划的应用领域总结 引言 动态规划(Dynamic Programming&#xff0c;简称DP)是一…

蓝桥杯备考------>双指针(滑动窗口)

来看哈我们这道例题 我们第一种想法应该就是暴力求解&#xff0c;枚举每个子数组 当我们枚举第一个数的时候&#xff0c;我们要从第一个数开始挨个枚举每个结尾 如图&#xff0c;以第一个数开头的最长不重复数我们就枚举完了 然后我们让两个指针全部到第二个数 再枚举第二个…

python实现股票数据可视化

最近在做一个涉及到股票数据清洗及预测的项目&#xff0c;项目中需要用到可视化股票数据这一功能&#xff0c;这里我与大家分享一下股票数据可视化的一些基本方法。 股票数据获取 目前&#xff0c;我已知的使用python来获取股票数据方式有以下三种: 爬虫获取&#xff0c;实现…

【15】Selenium 爬取实战

一、selenium适用场景 二、爬取目标 三、爬取列表页 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;加载列表页 &#xff08;3&#xff09;解析列表页 &#xff08;4&#xff09;main 四、爬取详情页 &#xff08;1&#xff09;加载详情页 &#xff08;2…

如何封装一个上传文件组件

#今天用el-upload感到很多不方便&#xff0c;遂决定自己封装一个。注&#xff1a;本文不提供表面的按钮样式和文件上传成功后的样式&#xff0c;需要自己创建。本文仅介绍逻辑函数# 1&#xff0c;准备几个表面用来指引上传的元素 2&#xff0c;创造统一的隐藏文件上传输入框&…

【计网】数据包

期末复习自用的&#xff0c;处理得比较草率&#xff0c;复习的同学或者想看基础的同学可以看看&#xff0c;大佬的话可以不用浪费时间在我的水文上了 1.数据包的定义&#xff1a; 数据包是网络通信中的基本单元&#xff0c;它包含了通过网络传输的所有必要信息。数据包的结构…

HTTP抓包Websocket抓包(Fiddler)

近期时常要和各个厂商的java云平台打交道&#xff1a;登录、上传、下载等&#xff0c;程序的日志虽必不可少&#xff0c;但前期调试阶段&#xff0c;免不了遇到问题&#xff0c;这时有一个称手的抓包工具就显得尤为重要了。 Fiddler Everywhere是一款跨平台的网络调试工具&…

Git和GitCode使用(从Git安装到上传项目一条龙)

第一步 菜鸟教程-Git教程 点击上方链接&#xff0c;完成Git的安装&#xff0c;并了解Git 工作流程&#xff0c;知道Git 工作区、暂存区和版本库的区别 第二步 GitCode官方帮助文档-SSH 公钥管理 点击上方链接&#xff0c;完成SSH公钥设置 第三步&#xff08;GitCode的官方引…

基于 WebAssembly 的 Game of Life 交互实现

一、前言 在前期的实现中&#xff0c;我们使用 Rust 编写核心逻辑&#xff0c;并通过 WebAssembly 将其引入到 Web 环境中&#xff0c;再利用 JavaScript 进行渲染。接下来&#xff0c;我们将在这一基础上增加用户交互功能&#xff0c;使模拟过程不仅能够自动演化&#xff0c;…

【keil】单步调试

一、步骤 1、打开stc-isp软件 2.打开keil仿真设置&#xff0c;选择对应的单片机型号 3.点击将所选目标单片机设置为仿真芯片&#xff0c;点击下载&#xff0c;按一下单片机打下载按钮 4.此时已经将仿真程序下载到单片机 5.此时点击options,找到debug选择STC Montor 51 Driv…

c++弱指针实现原理

在 C 中&#xff0c;弱指针&#xff08;std::weak_ptr&#xff09;是一种特殊的智能指针&#xff0c;其核心目标是‌解决 std::shared_ptr 的循环引用问题‌&#xff0c;同时不增加对象的引用计数。它的实现原理基于与 std::shared_ptr 共享的 ‌控制块&#xff08;Control Blo…

【ManiSkill】环境success条件和reward函数学习笔记

1. “PickCube-v1” info["success"]&#xff1a;用于指示任务是否成功完成 布尔型张量&#xff0c;在环境的evaluate()方法中计算并返回&#xff1a; "success": is_obj_placed & is_robot_static这确保了机器人不仅能将物体准确放置在目标位置&am…

用空闲时间做了一个小程序-二维码生成器

一直在摸鱼中赚钱的大家好呀~ 先向各位鱼友们汇报一下情况&#xff0c;目前小程序已经有900的鱼友注册使用过。虽然每天都有新的鱼友注册&#xff0c;但是鱼友增长的还很缓慢。自从国庆前的文字转语音的工具上线到现在已经将近有1个月没有更新小程序了。但是今天终终终终终于又…

31天Python入门——第14天:异常处理

你好&#xff0c;我是安然无虞。 文章目录 异常处理1. Python异常2. 异常捕获try-except语句捕获所有的异常信息获取异常对象finally块 3. raise语句4. 自定义异常5. 函数调用里面产生的异常补充练习 异常处理 1. Python异常 Python异常指的是在程序执行过程中发生的错误或异…

PyQt6实例_批量下载pdf工具_使用pyinstaller与installForge打包成exe文件

目录 前置&#xff1a; 步骤&#xff1a; step one 准备好已开发完毕的项目代码 step two 安装pyinstaller step three 执行pyinstaller pdfdownload.py&#xff0c;获取初始.spec文件 step four 修改.spec文件&#xff0c;将data文件夹加入到打包程序中 step five 增加…

Axure项目实战:智慧城市APP(完整交互汇总版)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;智慧城市APP 主要内容&#xff1a;主功能&#xff08;社保查询、医疗信息、公交查询等&#xff09;、活动、消息、我的页面汇总 应用场景&#xff…

Appium Inspector使用教程

1.下载最新版本 https://github.com/appium/appium-inspector/releases 2.本地启动一个Appium服务 若Android SDK已安装Appium服务&#xff0c;则在任意terminal使用appium启动服务即可 3.Appium Inspector客户端配置连接到Appium服务 Configuring and Starting a Session…

Pycharm(七):几个简单案例

一.剪刀石头布 需求&#xff1a;和电脑玩剪刀石头布游戏 考察点&#xff1a;1.随机数&#xff1b;2.判断语句 import random # numrandom.randint(1,3) # print(num) # print(**30) #1.录入玩家手势 playerint(input(请输入手势&#xff1a;&#xff08;1.剪刀 2.石头 3&…

Python Cookbook-4.13 获取字典的一个子集

任务 你有一个巨大的字典&#xff0c;字典中的一些键属于一个特定的集合&#xff0c;而你想创建一个包含这个键集合及其对应值的新字典。 解决方案 如果你不想改动原字典: def sub_dict(somedict,somekeys,default None):return dict([(k, somedict.get(k,default)) for k…