高通Android 11/12/13 通过包名设置默认launcher

2024-06-04 5589阅读

背景:最近在封装供第三应用系统SDK 接口,遇到一个无法通过包名设置主launcher代码坑所以记录下。

 

涉及类roles.xml # 
DeaultAppActivity.java#onCreate
DefaultAppChildFragment.java # onRoleChanged # addPreference
AutoDefaultAppListFragment#onActivityCreated
ManageRoleHolderStateLiveData.java #setRoleHolderAsUser
HandheldDefaultAppFragment.java(packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/handheld)#newInstance
RoleManager.java #addRoleHolderAsUser
IRoleManager.aidl #addRoleHolderAsUser
Role.java  #getDefaultHolders 
TwoTargetPreference.java #OnSecondTargetClickListener
PackageManagerShellCommand.java#runSetHomeActivity
ResolverActivity.java # onCreate
ParseActivityUtils #
    private static ParseResult parseActivityOrAlias(ParsedActivity activity,
            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
            ParseInput input, int parentActivityNameAttr, int permissionAttr,
            int exportedAttr)

1、一开始我是这样写的,代码如下图所示。

private void setDefaultLauncher3(Context context,String packageName,String className) {
     try {
         PackageManager pm = getPackageManager();
         Log.i("deflauncher", "deflauncher : PackageName = " + packageName + " ClassName = " + className);
         IntentFilter filter = new IntentFilter();
         filter.addAction("android.intent.action.MAIN");
         filter.addCategory("android.intent.category.HOME");
         filter.addCategory("android.intent.category.DEFAULT");
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.addCategory(Intent.CATEGORY_HOME);
         List list = pm.queryIntentActivities(intent, 0);
         final int N = list.size();
         ComponentName[] set = new ComponentName[N];
         int bestMatch = 0;
         for (int i = 0; i  bestMatch) bestMatch = r.match;
         }
         ComponentName preActivity = new ComponentName(packageName, className);
         pm.addPreferredActivity(filter, bestMatch, set, preActivity);
     } catch (Exception e) {
         e.printStackTrace();
     }
 }

2、具体添加位置参考在frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java

3、写***launcher包名和主activity类名方法如下代码所示 app.olauncher.MainActivity

/**
     * This method shares parsing logic between Activity/Receiver/alias instances, but requires
     * passing in booleans for isReceiver/isAlias, since there's no indicator in the other
     * parameters.
     *
     * They're used to filter the parsed tags and their behavior. This makes the method rather
     * messy, but it's more maintainable than writing 3 separate methods for essentially the same
     * type of logic.
     */
    @NonNull
    private static ParseResult parseActivityOrAlias(ParsedActivity activity,
            ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
            TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
            ParseInput input, int parentActivityNameAttr, int permissionAttr,
            int exportedAttr) throws IOException, XmlPullParserException {
        String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
        if (parentActivityName != null) {
            String packageName = pkg.getPackageName();
            String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
            if (parentClassName == null) {
                Log.e(TAG, "Activity " + activity.getName()
                        + " specified invalid parentActivityName " + parentActivityName);
            } else {
                activity.setParentActivity(parentClassName);
            }
        }
        String permission = array.getNonConfigurationString(permissionAttr, 0);
        if (isAlias) {
            // An alias will override permissions to allow referencing an Activity through its alias
            // without needing the original permission. If an alias needs the same permission,
            // it must be re-declared.
            activity.setPermission(permission);
        } else {
            activity.setPermission(permission != null ? permission : pkg.getPermission());
        }
        final boolean setExported = array.hasValue(exportedAttr);
        if (setExported) {
            activity.exported = array.getBoolean(exportedAttr, false);
        }
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG
                || parser.getDepth() > depth)) {
            if (type != XmlPullParser.START_TAG) {
                continue;
            }
            final ParseResult result;
            if (parser.getName().equals("intent-filter")) {
                ParseResult intentResult = parseIntentFilter(pkg, activity,
                        !isReceiver, visibleToEphemeral, resources, parser, input);
                if (intentResult.isSuccess()) {
                    ParsedIntentInfo intent = intentResult.getResult();
                    if (intent != null) {
						Log.e(TAG,"ZM activityName="+activity.getName());
				      if("app.olauncher.MainActivity".equals(activity.getName()))
                           {
                                intent.addCategory("android.intent.category.HOME");
                                intent.addCategory("android.intent.category.DEFAULT");
                                intent.setPriority(1000);
                           }
                        activity.order = Math.max(intent.getOrder(), activity.order);         
                        activity.addIntent(intent);
                        if (LOG_UNSAFE_BROADCASTS && isReceiver
                                && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
                            int actionCount = intent.countActions();
                            for (int i = 0; i  0;
            if (hasIntentFilters) {
                final ParseResult exportedCheckResult = input.deferError(
                        activity.getName() + ": Targeting S+ (version " + Build.VERSION_CODES.S
                        + " and above) requires that an explicit value for android:exported be"
                        + " defined when intent filters are present",
                        DeferredError.MISSING_EXPORTED_FLAG);
                if (exportedCheckResult.isError()) {
                    return input.error(exportedCheckResult);
                }
            }
            activity.exported = hasIntentFilters;
        }
        return input.success(activity);
    }

4、在ResolverActivity.java 中onCreate方法中 执行以下代码,代码路径 /frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

    protected void onCreate(Bundle savedInstanceState, Intent intent,
            CharSequence title, int defaultTitleRes, Intent[] initialIntents,
            List rList, boolean supportsAlwaysUseOption) {
        setTheme(appliedThemeResId());
        super.onCreate(savedInstanceState);
        if (mResolvingHome) {
            setDefaultLauncher3();
            finish();
            return;
        }

5、灵活一点如果动态设置launcher流程又不一样,下图是Setttings默认主屏幕应用  launcher列表选项(这个界面radiobutton控件通过preference动态添加 这个addPreference(preference):)

高通Android 11/12/13 通过包名设置默认launcher 第1张

6、点击事件位置代码路径packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java

 private void addPreference(@NonNull String key, @NonNull Drawable icon,
            @NonNull CharSequence title, boolean checked, @Nullable ApplicationInfo applicationInfo,
            @NonNull ArrayMap oldPreferences,
            @NonNull PreferenceScreen preferenceScreen, @NonNull Context context) {
        TwoStatePreference preference = (TwoStatePreference) oldPreferences.get(key);
        if (preference == null) {
            preference = requirePreferenceFragment().createApplicationPreference(context);
            preference.setKey(key);
            preference.setIcon(icon);
            preference.setTitle(title);
            preference.setPersistent(false);
            preference.setOnPreferenceChangeListener((preference2, newValue) -> false);
            preference.setOnPreferenceClickListener(this);
        }
          Log.e("DefaultAppChildFragment","addPreference");
          preference.setChecked(checked);
	   
        if (applicationInfo != null) {
            mRole.prepareApplicationPreferenceAsUser(preference, applicationInfo, mUser, context);
        }
        preferenceScreen.addPreference(preference);
    }

logcat日志

 DefaultAppChildFragment com.android.permissioncontroller     E  addPreference

7、另外一种通过指令去设置 adb shell pm set-home-activity  app.olauncher.debug (主launcher包名),验证过是没问题的。

8、实际调用还是通过RoleManager#addRoleHolderAsUser方法去添加为主Launcher

代码路径packages\modules\Permission\framework-s\java\android\app\role\RoleManager.java

  /**
     * Add a specific application to the holders of a role. If the role is exclusive, the previous
     * holder will be replaced.
     * 

* Note: 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 callback) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Objects.requireNonNull(user, "user cannot be null"); Objects.requireNonNull(executor, "executor cannot be null"); Objects.requireNonNull(callback, "callback cannot be null"); try { mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(), createRemoteCallback(executor, callback)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }

打印logcat日志如下所示

2024-05-14 01:18:43.314  1653-1653  RoleManager             pid-1653                             D  Package added as role holder, role: android.app.role.HOME, package: com.android.launcher3
2024-05-14 01:47:11.673  2854-23939 RoleContro...erviceImpl com.android.permissioncontroller     I  Package is already a role holder, package: com.android.launcher3, role: android.app.role.HOME
2024-05-14 01:47:11.674  1653-1653  RoleManager             pid-1653                             D  Package added as role holder, role: android.app.role.HOME, package: com.android.launcher3
2024-05-14 01:18:43.319  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:18:43.324  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep
2024-05-14 01:18:43.332  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:18:43.338  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep
2024-05-14 01:47:10.880  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=app.olauncher.debugtitle=Olauncher
2024-05-14 01:47:10.885  2854-2854  DefaultApp...ragment ZM com.android.permissioncontroller     E  key=com.android.launcher3title=Quickstep

9、代码路径 packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/role/ui/ManageRoleHolderStateLiveData.java

高通Android 11/12/13 通过包名设置默认launcher 第2张

10、代码路径frameworks\base\services\core\java\com/android\server\pm\PackageManagerShellCommand.java

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('/')  

11、最后可以把这些代码添加自己自定义系统服务AIDL接口 ,然后在Android.bp中添加源码编译路径(不知道怎么添加AIDL源码编译路径看我之前这篇文章高通 Android 12 源码编译aidl接口_安卓12 怎么写aidl-CSDN博客)

12、在自己app应用调用通过 如下代码 进行设置即可(Process导入android.os包切记哈)

/**
     * 设置当前Launcher
     *
     * @param packageName 传入第三方launcher包名
     */
    public void setCurrentLauncher(String packageName) {
        setRoleHolderAsUser(RoleManager.ROLE_HOME, packageName, 0, Process.myUserHandle(), mContext);
    }

13、最后别忘记如果你是app调用代码的时候记得加系统签名哈 AndroidManifest.xml中 ,否则也不会生效。

 android:sharedUserId="android.uid.system"

到这里基本结束了,转载请注明出处高通Android 11/12/13 通过包名设置默认launcher-CSDN博客,谢谢!

感谢

Android R设置默认桌面_setroleholderasuser-CSDN博客

Android10.0(Q) 默认应用设置(电话、短信、浏览器、主屏幕应用)_android.app.role.browser-CSDN博客


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]