关于Android 10以上无法隐藏应用图标问题探究及解决方案

Android 10之前的隐藏方式:

con.getPackageManager().setComponentEnabledSetting(new ComponentName(con,activityAliasName,PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);

Android 10之后,该隐藏图标方式失效,隐藏成功后依然会在桌面上保留其图标,点击后会跳转到应用详情页。具体查看官方API更新说明:getActivityList

那这就很难受了,在我看来,目前设计的并不太合理,能不能隐藏应该由用户决定,而不是强制不允许隐藏,不过目前官方也设置了控制开关,可以修改系统设置来去掉该限制,只不过需要adb或能修改系统设置的APP或ROOT权限才能修改。接下来就是具体分析了。

具体分析

  • getActivityList
//frameworks/base/core/java/android/content/pm/LauncherApps.java
    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
    
    
        logErrorForInvalidProfileAccess(user);
        try {
    
    
            return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
                    packageName, user), user);
        } catch (RemoteException re) {
    
    
            throw re.rethrowFromSystemServer();
        }
    }
  • getLauncherActivities
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
@Override
public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
        String callingPackage, String packageName, UserHandle user) throws RemoteException {
    
    
    //查询包含ACTION_MAIN和CATEGORY_LAUNCHER的activity
    ParceledListSlice<LauncherActivityInfoInternal> launcherActivities =
            queryActivitiesForUser(callingPackage,
                    new Intent(Intent.ACTION_MAIN)
                            .addCategory(Intent.CATEGORY_LAUNCHER)
                            .setPackage(packageName),
                    user);
    //检测系统设置(系统设置控制开关控制是否走新版限制逻辑)
    if (Settings.Global.getInt(mContext.getContentResolver(),
            Settings.Global.SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED, 1) == 0) {
    
    
        return launcherActivities;
    }
    //没有找到入口activity
    if (launcherActivities == null) {
    
    
        // Cannot access profile, so we don't even return any hidden apps.
        return null;
    }

    final int callingUid = injectBinderCallingUid();
    final long ident = injectClearCallingIdentity();
    try {
    
    
        //托管配置文件可以隐藏APP
        if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
    
    
            // Managed profile should not show hidden apps
            return launcherActivities;
        }
        //设备管理员可以隐藏APP
        if (mDpm.getDeviceOwnerComponentOnAnyUser() != null) {
    
    
            // Device owner devices should not show hidden apps
            return launcherActivities;
        }

        final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
                launcherActivities.getList());
        if (packageName != null) {
    
    
            // If this hidden app should not be shown, return the original list.
            // Otherwise, inject hidden activity that forwards user to app details page.
            //可能是如果大于0,表示有有效的启动入口,则不用注入隐藏的APP详情页入口
            if (result.size() > 0) {
    
    
                return launcherActivities;
            }
            final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(
                    packageName, /* flags= */ 0, callingUid, user.getIdentifier());
            //根据APP信息进行判断是否注入隐藏APP详情页入口
            if (shouldShowSyntheticActivity(user, appInfo)) {
    
    
                LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
                        callingUid, user);
                if (info != null) {
    
    
                    result.add(info);
                }
            }
            return new ParceledListSlice<>(result);
        }
        final HashSet<String> visiblePackages = new HashSet<>();
        for (LauncherActivityInfoInternal info : result) {
    
    
            visiblePackages.add(info.getActivityInfo().packageName);
        }
        final List<ApplicationInfo> installedPackages =
                mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
                        user.getIdentifier(), callingUid);
        for (ApplicationInfo applicationInfo : installedPackages) {
    
    
            if (!visiblePackages.contains(applicationInfo.packageName)) {
    
    
                if (!shouldShowSyntheticActivity(user, applicationInfo)) {
    
    
                    continue;
                }
                LauncherActivityInfoInternal info = getHiddenAppActivityInfo(
                        applicationInfo.packageName, callingUid, user);
                if (info != null) {
    
    
                    result.add(info);
                }
            }
        }
        return new ParceledListSlice<>(result);
    } finally {
    
    
        injectRestoreCallingIdentity(ident);
    }
}
  • shouldShowSyntheticActivity
//frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
private boolean shouldShowSyntheticActivity(UserHandle user, ApplicationInfo appInfo) {
    
    
    //没有app信息,是系统APP或者更新的系统APP,则不添加隐藏详情页入口
    if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
    
    
        return false;
    }
    //是管理员APP也不用添加该入口
    if (isManagedProfileAdmin(user, appInfo.packageName)) {
    
    
        return false;
    }
    final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);
    if (pkg == null) {
    
    
        // Should not happen, but we shouldn't be failing if it does
        return false;
    }
    // If app does not have any default enabled launcher activity or any permissions,
    // the app can legitimately have no icon so we do not show the synthetic activity.
    return requestsPermissions(pkg) && hasDefaultEnableLauncherActivity(
            appInfo.packageName);
}

根据以上源码,可以发现该机制有个全局设置开关,并且有多种情况可以不受该机制限制。因此总结出如下解决方案:

  • 若是你的APP有adb权限或ROOT权限,可以直接修改这个全局开关,修改命令如下:
adb shell settings put global show_hidden_icon_apps_enabled 0
  • 或者你的APP有设备管理员权限
  • 或者你的APP是系统APP
  • 或者你的APP没有任何默认启动入口
  • 或者你的APP没有请求任何权限
  • 或者你的APP涉及到Managed profile(这个具体不太清楚怎么用)
  • 或者你的APP是Xposed模块,可以直接注入系统服务拦截相关API,或者注入可以修改系统设置的APP(比如:设置)直接修改这个开关
  • 或者你是系统源码开发者,可以直接修改该处代码即可
  • 其它更多还未探索出的方式(比如利用某些逻辑漏洞)…

参考

猜你喜欢

转载自blog.csdn.net/qq_26914291/article/details/128359218