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

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

背景:最近在封装供第三应用系统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 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、Android10 一开始我是这样写的,代码如下图所示。

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、Android 10 具体添加位置参考在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<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、Android 10 在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、Android 12 点击事件位置代码路径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.
*


* 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

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 8.1 /9.0/10 设置默认Launhcer方法,在ResolveActivity中onCreate方法添加 ,代码示例如下所示

private void setDefaultLauncher(){
// 只需要修改此处即可
String defaultlauncherpckname = “xxx.xxx.xxx.xxx”;

PackageManager mPm = mContext.getPackageManager();
Intent mIntent = new Intent();
mIntent.setAction(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> mList = mPm.queryIntentActivities(mIntent, 0);
ComponentName[] mHomeComponentSet = new ComponentName[mList.size()];
ComponentName newHome = null;

for (int i = 0; i < mList.size(); i++) {
	 ActivityInfo info = mList.get(i).activityInfo;
	 ComponentName componentName = new ComponentName(info.packageName, info.name);
	 mHomeComponentSet[i] = componentName;
	 if (info.packageName.equals(defaultlauncherpckname)) {
		 newHome = componentName;
	 }
}
	
if (newHome != null) {
	IntentFilter mHomeFilter = new IntentFilter();
	mHomeFilter.addAction(Intent.ACTION_MAIN);
	mHomeFilter.addCategory(Intent.CATEGORY_HOME);
	mHomeFilter.addCategory(Intent.CATEGORY_DEFAULT);
	// Android8.1以后弃用了源码中的方法,如果依旧用会报错(笔者没办法调用老式)
	mPm.replacePreferredActivity(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY, mHomeComponentSet, newHome);
}

}
startHomeActivityLocked()方法中调用setDefaultLauncher即可

AI写代码
感谢

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

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

                        版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_15950325/article/details/138844489

相关文章:

  • [MySql] 多表关系, 多表查询
  • 消息中心系统架构设计
  • 14 配置Hadoop集群-配置历史和日志服务
  • Zemax与Matlab交互:双胶合优化详细流程
  • Qt图形化界面为何总被“冷落“?
  • IPv6协议
  • STM32 ADC转换完成回调函数详解 HAL_ADC_ConvCpltCallback与HAL_ADC_ConvHalfCpltCallback
  • 轮胎厂相关笔记
  • Rancher2.8.5架构
  • 如何把数据从SQLite迁移到PostgreSQL
  • c++ primer 阅读手记 第七章
  • 【蓝桥杯】 枚举和模拟练习题
  • 统一语言学习范式
  • 企业级海外网络专线行业应用案例及服务商推荐
  • element-ui图片查看器
  • idea导入tomcat的jar
  • 算法学习11——滑动窗口——最大连续1的个数
  • 兼职网|基于Java+vue的兼职网系统(源码+数据库+文档)
  • 交换机、路由器、VLAN、单臂路由、三层交换、STP
  • python学习笔记(6)运算符
  • 做最漂亮的网站/网上在哪里打广告最有效
  • 怎么介绍vue做的购物网站项目/百度400电话
  • 北京网站建设报价/企业查询系统
  • 基于html5的旅游网站的设计/个人网页在线制作
  • 专门做问卷的网站/seo技术优化技巧
  • 网站开发行业怎么样/河南优化网站