android 获取所有用户的应用程序列表

tgabmvqs  于 2023-04-28  发布在  Android
关注(0)|答案(5)|浏览(114)

如果我想检索当前用户的所有应用程序的ApplicationInfo列表,我可以运行:

PackageManager pkgmanager = ctx.getPackageManager();
List<ApplicationInfo> installedApps = pkgmanager.getInstalledApplications(PackageManager.GET_META_DATA);

但我正在寻找一种方法来获得这些名单为每个用户,而不仅仅是当前的一个。
根据我的理解,可以像这样检索用户ID:

List<Integer> userIds = new ArrayList<>();
PackageManager pkgmanager = ctx.getPackageManager();
final UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
List<UserHandle> list = um.getUserProfiles();

for (UserHandle user : list) {
    Matcher m = p.matcher(user.toString());
    if (m.find()) {
        int id = Integer.parseInt(m.group(1));
            userIds.add(id);
    }
}

现在我正在寻找这样的东西:

for (int i=0; i<userIds.size(); i++) {
    Integer userId = userIds.get(i)
    List<ApplicationInfo> installedApps = pkgmanager.getInstalledApplicationsByUserId(userId, PackageManager.GET_META_DATA);
}

显然getInstalledApplicationsByUserId不存在。
起初我以为getPackagesForUid可以解决这个问题,但事实证明,Linux意义上的“用户”和用户配置文件之间是有区别的。通常每个Android应用程序都在自己的用户下运行,以实现隔离。但可以在同一用户下运行两个应用程序,以便它们可以轻松访问彼此的数据。getPackagesForUid仅返回在给定用户ID下运行的所有应用程序的名称,这通常是一个。除了“用户”,还有“用户配置文件”,这就是我希望该方法所指的。也许我应该在代码中写userProfileId而不是userId

编辑:使用adb shell,我可以这样获取应用ID:

# Get user profile IDs
USER_PROFILE_IDS="$(pm list users | grep UserInfo | cut -d '{' -f2 | cut -d ':' -f1)"

# Iterate over user profile IDs
while read -r USER_PROFILE_ID ; do
    # List the packages for each user profile by ID
    PACKAGE_IDS_FOR_THIS_USER="$(pm list packages --user "$USER_PROFILE_ID" | cut -d ':' -f2)"
    echo "#######################################################################"
    echo "The user with id $USER_PROFILE_ID has the following packages installed:"
    echo "$PACKAGE_IDS_FOR_THIS_USER"
done <<< "$USER_PROFILE_IDS"

但这是Bash而不是Java。

Edit 2:如果我错了请纠正我(这是我第一次用Java编写代码),但似乎没有Java API来实现这一点。所以唯一的方法就是使用shell。所以这是我想到的:

import com.stericson.rootshell.RootShell;
import com.stericson.rootshell.execution.Command;
import com.stericson.rootshell.execution.Shell;
import com.stericson.roottools.RootTools;
...
public final class Api {
    ...
    /**
     * @param ctx application context (mandatory)
     * @return a list of user profile ids
     */
    private static List<Integer> getUserIds(Context ctx) {
        List<Integer> userIds = new ArrayList<>();
        PackageManager pkgmanager = ctx.getPackageManager();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //this code will be executed on devices running ICS or later
            final UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
            List<UserHandle> list = um.getUserProfiles();

            for (UserHandle user : list) {
                Matcher m = p.matcher(user.toString());
                if (m.find()) {
                    int id = Integer.parseInt(m.group(1));
                    //if (id > 0) {
                        userIds.add(id);
                    //}
                }
            }
        } else {
            userIds.add(0);
        }
        return userIds;
    }

    /**
     * @param ctx application context (mandatory)
     * @return a list of user profile ids
     */
    private static List<String> getPackageIdsByUserProfileId(Integer userId) {
        List<String> packageIds = new ArrayList<>();
        Command command = new Command(0, "pm list packages --user " + userId + " | cut -d ':' -f2 ")
        {
            @Override
            public void commandOutput(int id, String line) { 
                packageIds.add(line);
                super.commandOutput(id, line);
            }
        };
        Shell shell = RootTools.getShell(true);
        shell.add(command);

        while (!command.isFinished()) {
            Thread.sleep(100);
        }
        return packageIds;
    }
...
    /**
     * @param ctx application context (mandatory)
     * @return a list of applications
     */
    public static List<PackageInfoData> getApps(Context ctx, GetAppList appList) {
        List<Integer> userIds = getUserIds();
        for (int i=0; i<userIds.size(); i++) {
            Integer userId = userIds.get(i)
            List<String> packageIds = getPackageIdsByUserProfileId(userId)
        }
        ...
    }
}

但我不知道这是否接近实际工作的东西。除此之外,我只得到包ID(“com.whatsapp”等),但是我想得到一个ApplicationInfo的列表,就像getInstalledApplications返回它一样。我只是想不出一个好的方法来做到这一点。也许可以加载包清单,然后以某种方式基于它们创建ApplicationInfo示例?

Edit 3:我想我找到了source code for the pm executable

奇怪的是,我找不到一个单一的提到--user标志。
代码中最相关的部分是:

import android.os.ServiceManager;
...
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
...
int getFlags = 0;
...
final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags);

getInstalledPackages方法简单地调用mPm.getInstalledPackages,然后:

@SuppressWarnings("unchecked")
private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags)
        throws RemoteException {
    final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
    PackageInfo lastItem = null;
    ParceledListSlice<PackageInfo> slice;
    do {
        final String lastKey = lastItem != null ? lastItem.packageName : null;
        slice = pm.getInstalledPackages(flags, lastKey);
        lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
    } while (!slice.isLastSlice());
    return packageInfos;
}

这给我留下了比以前更多的问题。首先,我想知道我是否可以直接导入com.android.commands.pm类。其次,我想知道我如何告诉它返回特定用户配置文件的包,或者这是否是正确的源代码。最后,我想知道我是否需要root权限才能使用它。毕竟,仅对runRemoveProfilerunCreateProfilerunListProfiles执行if (Process.myUid() != ROOT_UID)检查。

Edit 4:找不到package服务的源代码,只找到了这个文件:/data/system/packages.xml。它包含所有软件包的一些基本软件包信息(针对所有用户配置文件),但它既不包含实际的应用程序名称,也不包含它们属于哪些用户配置文件的信息。
Edit 5:我想我找到了package service source code,我觉得这个方法比较重要:

public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId)

不幸的是,我只是不理解代码。对我来说,它看起来像是包以某种方式来自mSettings.mPackages。该变量在代码注解中解释如下:
{@link #mPackages}用于保护所有内存中解析的包详细信息和其他相关状态。它是一个细粒度的锁,只应暂时持有,因为它是系统中竞争最激烈的锁之一。

Edit 6:我在这个文件中发现了另一个更有趣的方法:

public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId)

我不知道ParceledListSlice是什么,但它说<ApplicationInfo>我假设这真的很接近我想要的List<ApplicationInfo>

ebdffaop

ebdffaop1#

我找到了ParceledListSlicehere的代码,以及它实现hereParcelable接口的代码。看起来它在内部使用了某种形式的数组,所以你应该能够迭代它。我不能说这对你是否有帮助,因为我对这些库不是很熟悉,但我希望这是个起点祝你好运!

nsc4cvqm

nsc4cvqm2#

您可以应用您在第一次EDIT和EDIT 2中提到的方法,并使用Android的shell,因为您提到您的设备具有root权限。
顺便说一下,我不熟悉您正在使用的“stericson”库,但我同意利用现有库使您的生活更轻松;我使用的是libsuperuser。否则,你可以自己编写代码来使用Process等运行shell命令(但是在处理错误输出、关闭对象等方面需要考虑很多事情)。

private List<String> listAllInstalledApps(){
    String user = "0"; //can modify to iterate over all users 
    if (Shell.SU.available()) {
        //grab user installed package names
        List<String> output =
            Shell.SU.run("pm list packages --user " + user + " -3");
        StringBuilder csvBuilder = new StringBuilder();

        for(String item : output){
            csvBuilder.append(item);
            csvBuilder.append(", ");
        }
        Log.info(TAG, "Obtained installed apps: " + csvBuilder.toString());
        return output;
    }
    return new ArrayList<>();
}

当然,你也可以根据需要使用其他参数来pm,比如-e, -d参见documentation
提取包名后,使用 dumpsys 获取所有必需的信息(如ApplicationInfo中所包含的信息)

//package names extracted from List<String> you returned earlier
for( String name : packageNames ){
    List<String> appInfo =
            Shell.SU.run("dumpsys package " + name);
    //search appInfo for info you need e.g. grantedPermissions, targetSdk...
}

如果您有专门需要此对象类型的下游代码,则根据需要创建ApplicationInfo对象

ApplicationInfo info = new ApplicationInfo();
info.uid = uid;
info.processName = processName;
info.className = classname;
info.packageName = packageName;
info.minSdkVersion = minSdkVersion;
//etc...

ApplicationInfo类的字段是公共的)。这也适用于PackageInfo,它是一个包含ApplicationInfo字段的对象,另外它包含安装时间等的详细信息,这些也是您可以从dumpsys输出中提取的详细信息。因此您可以根据需要创建一个数组,并可以使用新的ApplicationInfo对象进行填充。

tjvv9vkg

tjvv9vkg3#

如果您能够访问隐藏的API,因为您是平台应用程序或正在使用内省(具有正确的权限!),您可以简单地使用UserManager.getUsers()PackageManager. getInstalledApplicationsAsUser()

Set<ApplicationInfo> installedApplications = new HashSet<>();
for (UserInfo user : getSystemService(UserManager.class).getUsers()) {
    installedApplications.addAll(getPackageManager().getInstalledApplicationsAsUser(PackageManager.GET_META_DATA, user.id));
}
zd287kbt

zd287kbt4#

private static JSONArray getAllAppNames() {
        JSONArray appNameList = new JSONArray();
        List<PackageInfo> packs = GlobalApplication.getContextOfApplication().getPackageManager().getInstalledPackages(0);
        for (int i = 0; i < packs.size(); i++) {
            PackageInfo p = packs.get(i);
            if ((!isSystemPackage(p))) {
                String appName = p.applicationInfo.loadLabel(GlobalApplication.getContextOfApplication().getPackageManager()).toString();
                appNameList.put(appName);
            }
        }
        return appNameList;
    }

    private static boolean isSystemPackage(PackageInfo pkgInfo) {
        return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

这应该或多或少解决了你的基本问题

gab6jxml

gab6jxml5#

以编程方式获取已安装的应用程序列表
添加代码到activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    tools:context=".MainActivity"
    android:background="#8fa485">

    <ListView
        android:id="@+id/lv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</RelativeLayout>

并将代码添加到MainActivity.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Get the application context
        mContext = getApplicationContext();

        // Get the activity
        mActivity = MainActivity.this;

        // Get the widgets reference from XML layout
        mRelativeLayout = (RelativeLayout) findViewById(R.id.rl);
        mListView = (ListView) findViewById(R.id.lv);

        // Initialize a new Intent which action is main
        Intent intent = new Intent(Intent.ACTION_MAIN,null);

        // Set the newly created intent category to launcher
        intent.addCategory(Intent.CATEGORY_LAUNCHER);

        // Set the intent flags
        intent.setFlags(
                Intent.FLAG_ACTIVITY_NEW_TASK|
                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
        );

        // Generate a list of ResolveInfo object based on intent filter
        List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(intent,0);

        // Initialize a new ArrayList for holding non system package names
        List<String> packageNames = new ArrayList<>();

        // Loop through the ResolveInfo list
        for(ResolveInfo resolveInfo : resolveInfoList){
            // Get the ActivityInfo from current ResolveInfo
            ActivityInfo activityInfo = resolveInfo.activityInfo;

            // If this is not a system app package
            if(!isSystemPackage(resolveInfo)){
                // Add the non system package to the list
                packageNames.add(activityInfo.applicationInfo.packageName);
            }
        }

        // Initialize an ArrayAdapter using non system package names
        ArrayAdapter adapter = new ArrayAdapter<String>(
                mContext,
                android.R.layout.simple_list_item_1,
                packageNames
        );

        // DataBind the ListView with adapter
        mListView.setAdapter(adapter);

        // Set an item click listener for the ListView widget
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                // Get the ListView selected item
                String selectedItem = (String) adapterView.getItemAtPosition(i);

                // Display a Toast notification for clicked item
                Toast.makeText(mContext,"CLICKED : " + selectedItem,Toast.LENGTH_SHORT).show();

                // Get the intent to launch the specified application
                Intent intent = getPackageManager().getLaunchIntentForPackage(selectedItem);
                if(intent != null){
                    startActivity(intent);
                }else {
                    Toast.makeText(mContext,selectedItem + " Launch Error.",Toast.LENGTH_SHORT).show();
                }
            }
        });

   }

    // Custom method to determine an app is system app
    private boolean isSystemPackage(ResolveInfo resolveInfo){
       return ((resolveInfo.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
    }
}

你可以很容易地看到安装在手机上的应用程序的图标,只需付出一点努力,并添加ImageView。

相关问题