Почему Android PackageInstaller не работает? - PullRequest
0 голосов
/ 18 апреля 2020

Моя Android активность / приложение использует DownloadManager и PackageInstaller для обновления. Я опубликую соответствующий код здесь, но суть в том, что файл успешно загружается, но не устанавливается, и в LogCat нет полезной информации, которая бы помогла мне понять, почему.

Если с моим кодом что-то не так, я Мне бы очень хотелось это знать, но, насколько я знаю, я поступаю так, как говорят все примеры. Я попытался сделать это с и без установки DeviceOwner, и вижу то же самое поведение в любом случае. Ничего не происходит, и в LogCat нет ничего, что указывало бы на то, что пошло не так.

Я попытался подключить метод обратного вызова сеанса, но не могу получить какую-либо информацию, потому что мой 'sessionInfo' всегда равен NULL. Как это вообще возможно?

pkgInstaller.registerSessionCallback(new PackageInstaller.SessionCallback() {
    PackageInstaller.SessionInfo sessionInfo = pkgInstaller.getSessionInfo(sID);
}

Часть загрузки работает успешно, поэтому я просто покажу соответствующую строку здесь.

File downloadFile = new File(getExternalFilesDir(null),"myapp-update.apk");

apk была построена в Android Studio с использованием «Build -> Build Bundle (s) -> APK (s) -> Build APK (s)», который выдает «отладочный» APK (так же, как если бы я собирал и развертывал в устройство из IDE). Он был создан с НОВЫМИ VersionID и VersionName, а затем загружен на мой файловый сервер. Мое Android приложение загружает его с помощью DownloadManager, и оно работает.

Вот что происходит после загрузки APK.

private BroadcastReceiver onDownloadComplete = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        // Get our download record, verify it, etc.
        // ......

        // Get URI of downloaded file
        int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
        String apkStr = cursor.getString(uriIndex);

        // Install APK update
        Uri apkUri = Uri.parse(apkStr);
        installApk(Objects.requireNonNull(apkUri.getPath()));
    }
}

Вот метод, который устанавливает APK после загрузки.

public static final String ACTION_INSTALL_COMPLETE = BuildConfig.APPLICATION_ID + ".INSTALL_COMPLETE";
private void installApk(String apkFilePath)
{
    try
    {
        // Create package manager installation params
        PackageInstaller pkgInstaller = getBaseContext().getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.setAppPackageName(this.getPackageName());

        // Create package manager installation session
        int sessionId = pkgInstaller.createSession(params);
        PackageInstaller.Session session = pkgInstaller.openSession(sessionId);

        // Get size of downloaded APK
        long sizeBytes = 0;
        final File file = new File(apkFilePath);
        if (file.isFile())
            sizeBytes = file.length();

        // Stream downloaded APK file to package manager session
        InputStream in = new FileInputStream(apkFilePath);
        OutputStream out = session.openWrite("konektv_update_session", 0, -1);
        byte[] buffer = new byte[65536];
        int bytesRead = in.read(buffer);
        while (bytesRead != -1)
        {
            out.write(buffer, 0, bytesRead);
            bytesRead = in.read(buffer);
        }

        // Close streams
        session.fsync(out);
        out.close();
        in.close();

        // Close session
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, sessionId, new Intent(ACTION_INSTALL_COMPLETE),0);
        session.commit(pendingIntent.getIntentSender());
        session.close();

    }
    catch (Exception ex)
    {
       ex.printStackTrace();
    }
}

Мой специальный приемник вещания для установки APK выглядит следующим образом. Он никогда не вызывается, и загруженный APK не установлен. Я могу сказать, потому что ничего не происходит, но даже если это так, я отображаю номер версии, и он не меняется.

public class UpdateReceiver extends BroadcastReceiver
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        final String LOG_TAG = "myapp-updated";
        if (intent == null)
            return;

        String action = intent.getAction();
        if ((action == null) || !action.equalsIgnoreCase(Intent.ACTION_MY_PACKAGE_REPLACED))
            return;

        // Start main activity
        Intent intentMain = new Intent(context, MainActivity.class);
        intentMain.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intentMain.putExtra(KonekTvApplication.IS_BOOT_AUTOSTART, true);
        context.startActivity(intentMain);
    }
}

Вот соответствующие биты из моего файла AndroidManifest. xml.

    <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />

    <application
        android:testOnly="true"
        android:name=".MyApplication">

        <receiver
            android:name=".UpdateReceiver">
            <intent-filter >
                <action  android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
            </intent-filter>
        </receiver>

Единственный другой код связан с DeviceOwner, но эта методология должна работать независимо от владельца устройства. Единственная разница заключается в том, является ли обновление тихим или требует взаимодействия с пользователем.

public class DeviceOwnerReceiver extends DeviceAdminReceiver
{
    private static final String LOG_TAG = "my-owner-receiver";

    @Override
    public void onEnabled(Context context, Intent intent) {
        Log.w(LOG_TAG,"Device owner enabled");
    }

    public static ComponentName getComponentName(Context context) {
        return new ComponentName(context.getApplicationContext(), DeviceOwnerReceiver.class);
    }
}

Вот информация о владельце устройства из AndroidManifest. xml.

    <receiver
        android:name=".DeviceOwnerReceiver"
        android:label="@string/app_name"
        android:permission="android.permission.BIND_DEVICE_ADMIN">
        <meta-data
            android:name="android.app.device_admin"
            android:resource="@xml/device_admin" />
        <intent-filter>
            <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
        </intent-filter>
    </receiver>

А вот device_admin. xml file.

<?xml version="1.0" encoding="utf-8"?>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-policies>
    </uses-policies>
</device-admin>

Большое спасибо за любую помощь, которую вы можете предоставить.

...