web-dev-qa-db-ja.com

すべてのユーザーのアプリのリストを取得します

現在のユーザーのすべてのアプリの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アプリは分離目的で自身のユーザーで実行されます。ただし、同じユーザーで2つのアプリを実行して、お互いのデータに簡単にアクセスできるようにすることができます。getPackagesForUidは単に特定のユーザーIDで実行されるすべてのアプリの名前(通常は1つだけ)。「ユーザー」に加えて、メソッドが参照していたことを期待していた「ユーザープロファイル」もあります。多分、userProfileId私のコードではuserIdの代わりに。

Edit: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"
_

しかし、それはJavaではなくBashです...

Edit2:私が間違っている場合は修正してください(Javaでコードを記述したのはこれが初めてです)が、このようには表示されません。 a Javaこれを行うためのAPI。これを行う唯一の方法は、シェルを使用することです。したがって、これが私が思いついたものです。

_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"など)のみを取得しますが、getInstalledApplicationsが返すように ApplicationInfo のリストを取得します。これを行うための良い方法は考えられません。おそらく、パッケージマニフェストを読み込んで、それに基づいてApplicationInfoインスタンスを何らかの方法で作成することは可能でしょうか?

Edit3pm実行可能ファイルのソースコード を見つけたと思います。

奇妙なことに、その中に_--user_フラグの記述が1つも見つかりませんでした。

コードの最も関連する部分は次のとおりです。

_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権限も必要かと思います。結局のところ、if (Process.myUid() != ROOT_UID)チェックは、runRemoveProfilerunCreateProfile、およびrunListProfilesに対してのみ実行されます。

Edit4packageサービスのソースコードが見つかりませんでした。次のファイルしか見つかりませんでした:_/data/system/packages.xml_。 (すべてのユーザープロファイルの)すべてのパッケージに関するいくつかの基本的なパッケージ情報が含まれていますが、実際のアプリ名も含まれておらず、所属するユーザープロファイルに関する情報も含まれていません。

Edit5package service source code を見つけたと思います。この方法は重要だと思います:

_public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId)
_

残念ながら、私はコードを理解できません。私には、パッケージが何らかの形で_mSettings.mPackages_から提供されているように見えます。変数は、コードコメントで次のように説明されています。

{@link #mPackages}は、メモリ内で解析されたすべてのパッケージの詳細およびその他の関連する状態を保護するために使用されます。これは、システムで最も競合するロックの1つであるため、一時的に保持する必要がある細粒度のロックです。

Edit6:そのファイルでさらに興味深いメソッドを見つけました:

_public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId)
_

ParceledListSliceが何であるかはわかりませんが、_<ApplicationInfo>_と表示されているので、これは、必要な_List<ApplicationInfo>_形式に本当に近いと想定しています。しかし、それでも、私はその機能にアクセスする方法を完全に無知です。

24
Forivin

ParceledListSlicehere のコードと、それが実装するParcelableインターフェイスのコード here を見つけました。内部では何らかの形式の配列を使用しているように見えるので、それを反復できるはずです。私はこれらのライブラリーにあまり詳しくないので、これがあなたに役立つかどうかは実際には言えませんが、それが出発点であることを願っています。幸運を!

4
NightDice

最初のEDITおよびEDIT2で言及したアプローチを適用し、Androidのシェルを使用できます。これは、デバイスにroot権限があることを言及したためです。

ちなみに、私はあなたが使用している "stericson"のライブラリについてはよく知りませんが、既存のライブラリを活用してあなたの人生を楽にすることに同意します。 libsuperuser を使用しています。それ以外の場合は、自分でProcessなどを使用してシェルコマンドを実行するコードを記述できます(ただし、エラー出力の処理やオブジェクトのクローズなどについては、考慮すべきことがたくさんあります)。

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<>();
}

もちろん、必要に応じて-e, -dのような他の引数をpmに使用できます 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オブジェクトを入力できます。

3
dr_g

インストールされているアプリのリストをプログラムで取得する

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を追加するだけで、携帯電話にインストールされているアプリのアイコンを簡単に確認できます。

0
Amirhf
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;
    }

これは多かれ少なかれあなたの基本的な問題を解決するはずです

0
Anubhav Malik