赞
踩
背景:最近在封装供第三应用系统SDK 接口,遇到一个无法通过包名设置主launcher代码坑所以记录下。
- 涉及类roles.xml # <!---
- ~ @see com.android.settings.applications.defaultapps.DefaultHomePreferenceController
- ~ @see com.android.settings.applications.defaultapps.DefaultHomePicker
- ~ @see com.android.server.pm.PackageManagerService#setHomeActivity(ComponentName, int)
- -->
-
- 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<ParsedActivity> 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<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
- final int N = list.size();
- ComponentName[] set = new ComponentName[N];
- int bestMatch = 0;
- for (int i = 0; i < N; i++) {
- ResolveInfo r = list.get(i);
- set[i] = new ComponentName(r.activityInfo.packageName,
- r.activityInfo.name);
- if (r.match > 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<ParsedActivity> 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<ParsedIntentInfo> 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 < actionCount; i++) {
- final String action = intent.getAction(i);
- if (action == null || !action.startsWith("android.")) {
- continue;
- }
-
- if (!SAFE_BROADCASTS.contains(action)) {
- Slog.w(TAG,
- "Broadcast " + action + " may never be delivered to "
- + pkg.getPackageName() + " as requested at: "
- + parser.getPositionDescription());
- }
- }
- }
- }
- }
- result = intentResult;
- } else if (parser.getName().equals("meta-data")) {
- result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
- } else if (parser.getName().equals("property")) {
- result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
- } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
- ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
- true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
- resources, parser, input);
- if (intentResult.isSuccess()) {
- ParsedIntentInfo intent = intentResult.getResult();
- if (intent != null) {
- pkg.addPreferredActivityFilter(activity.getClassName(), intent);
- }
- }
- result = intentResult;
- } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
- ParseResult<ActivityInfo.WindowLayout> layoutResult =
- parseActivityWindowLayout(resources, parser, input);
- if (layoutResult.isSuccess()) {
- activity.windowLayout = layoutResult.getResult();
- }
- result = layoutResult;
- } else {
- result = ParsingUtils.unknownTag(tag, pkg, parser, input);
- }
-
- if (result.isError()) {
- return input.error(result);
- }
- }
-
- if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK
- && activity.metaData != null && activity.metaData.containsKey(
- ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
- final String launchMode = activity.metaData.getString(
- ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
- if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
- activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;
- }
- }
-
- ParseResult<ActivityInfo.WindowLayout> layoutResult =
- resolveActivityWindowLayout(activity, input);
- if (layoutResult.isError()) {
- return input.error(layoutResult);
- }
- activity.windowLayout = layoutResult.getResult();
-
- if (!setExported) {
- boolean hasIntentFilters = activity.getIntents().size() > 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<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
- setTheme(appliedThemeResId());
- super.onCreate(savedInstanceState);
-
- if (mResolvingHome) {
- setDefaultLauncher3();
- finish();
- return;
- }
5、灵活一点如果动态设置launcher流程又不一样,下图是Setttings默认主屏幕应用 launcher列表选项(这个界面radiobutton控件通过preference动态添加 这个addPreference(preference):)
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<String, Preference> 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.
- * <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) {
- 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
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('/') < 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;
- }
- }
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博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。