Programing

프로그래밍 방식으로 APK 설치 / 제거 (PackageManager 및 의도)

lottogame 2020. 6. 24. 07:57
반응형

프로그래밍 방식으로 APK 설치 / 제거 (PackageManager 및 의도)


내 응용 프로그램은 다른 응용 프로그램을 설치하며 설치된 응용 프로그램을 추적해야합니다. 물론 설치된 응용 프로그램 목록을 유지하면됩니다. 그러나 이것은 필요하지 않습니다! installedBy (a, b) 관계를 유지하는 것은 PackageManager의 책임입니다. 실제로 API에 따르면 다음과 같습니다.

public abstract String getInstallerPackageName (String packageName)- 패키지를 설치 한 응용 프로그램의 패키지 이름을 검색합니다. 이것은 패키지가 어느 시장에서 왔는지 식별합니다.

현재 접근

의도를 사용하여 APK 설치

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
startActivity(intent);

의도를 사용하여 APK 제거 :

Intent intent = new Intent(Intent.ACTION_DELETE, Uri.fromParts("package",
getPackageManager().getPackageArchiveInfo(apkUri.getPath(), 0).packageName,null));
startActivity(intent);

예를 들어 Android 마켓에서 패키지를 설치 / 제거하는 방식은 아닙니다. 더 풍부한 버전의 PackageManager를 사용합니다. Android Git 리포지토리에서 Android 소스 코드를 다운로드하면 알 수 있습니다. 아래는 의도 접근 방식에 해당하는 두 가지 숨겨진 방법입니다. 불행히도 외부 개발자는 사용할 수 없습니다. 그러나 아마도 그들은 미래에 있을까요?

더 나은 접근법

PackageManager를 사용하여 APK 설치

/**
 * @hide
 * 
 * Install a package. Since this may take a little while, the result will
 * be posted back to the given observer.  An installation will fail if the calling context
 * lacks the {@link android.Manifest.permission#INSTALL_PACKAGES} permission, if the
 * package named in the package file's manifest is already installed, or if there's no space
 * available on the device.
 *
 * @param packageURI The location of the package file to install.  This can be a 'file:' or a
 * 'content:' URI.
 * @param observer An observer callback to get notified when the package installation is
 * complete. {@link IPackageInstallObserver#packageInstalled(String, int)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK},
 * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}.
 * @param installerPackageName Optional package name of the application that is performing the
 * installation. This identifies which market the package came from.
 */
public abstract void installPackage(
        Uri packageURI, IPackageInstallObserver observer, int flags,
        String installerPackageName);

PackageManager를 사용하여 APK 제거

/**
 * Attempts to delete a package.  Since this may take a little while, the result will
 * be posted back to the given observer.  A deletion will fail if the calling context
 * lacks the {@link android.Manifest.permission#DELETE_PACKAGES} permission, if the
 * named package cannot be found, or if the named package is a "system package".
 * (TODO: include pointer to documentation on "system packages")
 *
 * @param packageName The name of the package to delete
 * @param observer An observer callback to get notified when the package deletion is
 * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be
 * called when that happens.  observer may be null to indicate that no callback is desired.
 * @param flags - possible values: {@link #DONT_DELETE_DATA}
 *
 * @hide
 */
public abstract void deletePackage(
        String packageName, IPackageDeleteObserver observer, int flags);

차이점

  • 인 텐트를 사용할 때 로컬 패키지 관리자는 설치가 시작된 응용 프로그램을 인식하지 못합니다. 특히 getInstallerPackageName (...)은 null을 반환합니다.

  • 숨겨진 메소드 installPackage (...)는 설치 프로그램 패키지 이름을 매개 변수로 사용하며이 값을 설정할 수 있습니다.

질문

의도를 사용하여 패키지 설치 프로그램 이름을 지정할 수 있습니까? (설치 패키지의 이름을 설치 의도에 추가로 추가 할 수 있습니까?)

팁 : Android 소스 코드를 다운로드하려면 여기에 설명 된 단계를 수행하십시오. 소스 트리 다운로드. * .java 파일을 추출하여 패키지 계층에 따라 폴더에 넣으려면 다음과 같은 깔끔한 스크립트를 확인할 수 있습니다 . Eclipse에서 Android 소스 코드보기 .


현재 타사 응용 프로그램에서는 사용할 수 없습니다. 리플렉션 또는 기타 트릭을 사용하여 installPackage ()에 액세스해도 시스템 응용 프로그램 만 사용할 수 있으므로 도움이되지 않습니다. (사용자가 권한을 승인 한 후에는 저수준 설치 메커니즘이기 때문에 일반 응용 프로그램이 액세스하기에 안전하지 않습니다.)

또한 installPackage () 함수 인수는 종종 플랫폼 릴리스간에 변경되었으므로 액세스하려고 시도하는 모든 다른 버전의 플랫폼에서 실패합니다.

편집하다:

또한이 installerPackage는 플랫폼 (2.2?)에 상당히 최근에 추가되었으며 원래 누가 앱을 설치했는지 추적하는 데 사용되지 않았습니다. 플랫폼에서 버그를보고 할 때 누가 시작 해야하는지 결정하는 데 사용됩니다 Android 피드백을 구현하기위한 앱 (이는 API 메소드 인수가 변경된 시간 중 하나이기도합니다.) 도입 된 지 오래 동안 마켓은 여전히 ​​설치된 앱을 추적하는 데이를 사용하지 않았습니다 (아직도 여전히 사용하지 않을 수 있음) ) 대신 Android 피드백 앱 (Market과 별 개인)을 "소유자"로 설정하여 피드백을 처리했습니다.


Android P +는 AndroidManifest.xml에서이 권한이 필요합니다.

<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

그때:

Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:com.example.mypackage"));
startActivity(intent);

제거합니다. 더 쉬운 것 같습니다 ...


API 레벨 14에는 ACTION_INSTALL_PACKAGEACTION_UNINSTALL_PACKAGE의 두 가지 새로운 조치가 도입되었습니다 . 이러한 조치를 통해 EXTRA_RETURN_RESULT 부울 을 전달 하여 설치 결과 알림을받을 수 있습니다.

설치 제거 대화 상자를 호출하기위한 예제 코드 :

String app_pkg_name = "com.example.app";
int UNINSTALL_REQUEST_CODE = 1;

Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);  
intent.setData(Uri.parse("package:" + app_pkg_name));  
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
startActivityForResult(intent, UNINSTALL_REQUEST_CODE);

Activity # onActivityResult 메소드 에서 알림을 받으십시오 .

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == UNINSTALL_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {
            Log.d("TAG", "onActivityResult: user accepted the (un)install");
        } else if (resultCode == RESULT_CANCELED) {
            Log.d("TAG", "onActivityResult: user canceled the (un)install");
        } else if (resultCode == RESULT_FIRST_USER) {
            Log.d("TAG", "onActivityResult: failed to (un)install");
        }
    }
}

장치 소유자 (또는 프로필 소유자 인 경우 시도하지 않은) 권한이 있으면 장치 소유자 API를 사용하여 패키지를 자동으로 설치 / 제거 할 수 있습니다.

제거를 위해 :

public boolean uninstallPackage(Context context, String packageName) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        int sessionId = 0;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        packageInstaller.uninstall(packageName, PendingIntent.getBroadcast(context, sessionId,
                new Intent("android.intent.action.MAIN"), 0).getIntentSender());
        return true;
    }
    System.err.println("old sdk");
    return false;
}

패키지를 설치하려면 :

public boolean installPackage(Context context,
                                     String packageName, String packagePath) {
    ComponentName name = new ComponentName(MyAppName, MyDeviceAdminReceiver.class.getCanonicalName());
    PackageManager packageManger = context.getPackageManager();
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        PackageInstaller packageInstaller = packageManger.getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(packageName);
        try {
            int sessionId = packageInstaller.createSession(params);
            PackageInstaller.Session session = packageInstaller.openSession(sessionId);
            OutputStream out = session.openWrite(packageName + ".apk", 0, -1);
            readTo(packagePath, out); //read the apk content and write it to out
            session.fsync(out);
            out.close();
            System.out.println("installing...");
            session.commit(PendingIntent.getBroadcast(context, sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
    System.err.println("old sdk");
    return false;
}

이러한 방법에 액세스하는 유일한 방법은 리플렉션입니다. 이러한 메소드를 PackageManager호출 getApplicationContext().getPackageManager()하고 리플렉션 액세스를 사용하여 오브젝트에 대한 핸들을 얻을 수 있습니다 . 체크 아웃 튜토리얼을.


According to Froyo source code, the Intent.EXTRA_INSTALLER_PACKAGE_NAME extra key is queried for the installer package name in the PackageInstallerActivity.


On a rooted device, you might use:

String pkg = context.getPackageName();
String shellCmd = "rm -r /data/app/" + pkg + "*.apk\n"
                + "rm -r /data/data/" + pkg + "\n"
                // TODO remove data on the sd card
                + "sync\n"
                + "reboot\n";
Util.sudo(shellCmd);

Util.sudo() is defined here.


If you are passing package name as parameter to any of your user defined function then use the below code :

    Intent intent=new Intent(Intent.ACTION_DELETE);
    intent.setData(Uri.parse("package:"+packageName));
    startActivity(intent);

If you're using Kotlin, API 14+, and just wish to show uninstall dialog for your app:

startActivity(Intent(Intent.ACTION_UNINSTALL_PACKAGE).apply {
    data = Uri.parse("package:$packageName")
})

You can change packageName to any other package name if you want to prompt the user to uninstall another app on the device


Prerequisite:

Your APK needs to be signed by system as correctly pointed out earlier. One way to achieve that is building the AOSP image yourself and adding the source code into the build.

Code:

Once installed as a system app, you can use the package manager methods to install and uninstall an APK as following:

Install:

public boolean install(final String apkPath, final Context context) {
    Log.d(TAG, "Installing apk at " + apkPath);
    try {
        final Uri apkUri = Uri.fromFile(new File(apkPath));
        final String installerPackageName = "MyInstaller";
        context.getPackageManager().installPackage(apkUri, installObserver, PackageManager.INSTALL_REPLACE_EXISTING, installerPackageName);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

Uninstall:

public boolean uninstall(final String packageName, final Context context) {
    Log.d(TAG, "Uninstalling package " + packageName);
    try {
        context.getPackageManager().deletePackage(packageName, deleteObserver, PackageManager.DELETE_ALL_USERS);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

To have a callback once your APK is installed/uninstalled you can use this:

/**
 * Callback after a package was installed be it success or failure.
 */
private class InstallObserver implements IPackageInstallObserver {

    @Override
    public void packageInstalled(String packageName, int returnCode) throws RemoteException {

        if (packageName != null) {
            Log.d(TAG, "Successfully installed package " + packageName);
            callback.onAppInstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to install package.");
            callback.onAppInstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback after a package was deleted be it success or failure.
 */
private class DeleteObserver implements IPackageDeleteObserver {

    @Override
    public void packageDeleted(String packageName, int returnCode) throws RemoteException {
        if (packageName != null) {
            Log.d(TAG, "Successfully uninstalled package " + packageName);
            callback.onAppUninstalled(true, packageName);
        } else {
            Log.e(TAG, "Failed to uninstall package.");
            callback.onAppUninstalled(false, null);
        }
    }

    @Override
    public IBinder asBinder() {
        return null;
    }
}

/**
 * Callback to give the flow back to the calling class.
 */
public interface InstallerCallback {
    void onAppInstalled(final boolean success, final String packageName);
    void onAppUninstalled(final boolean success, final String packageName);
}

참고URL : https://stackoverflow.com/questions/6813322/install-uninstall-apks-programmatically-packagemanager-vs-intents

반응형