简体   繁体   English

在没有 Google Play 访问权限的情况下远程更新我的应用

[英]Updating my app remotely without Google Play access

I am working on an app for my company's internal use which will collect performance stats from network and post them on our Grafana server.我正在开发一个供我公司内部使用的应用程序,它将从网络收集性能统计信息并将它们发布到我们的 Grafana 服务器上。 The app works fine with this context, but there is a problem: App will run on a phone at a datacenter and it will be very difficult to access it if we need to update the app for adding features.该应用程序在此上下文中运行良好,但存在一个问题:应用程序将在数据中心的手机上运行,​​如果我们需要更新应用程序以添加功能,将很难访问它。 Also the phone will not have internet access.此外,手机将无法访问互联网。 So I won't be able to update the app manually , or using Google Play.所以我将无法手动更新应用程序,或使用 Google Play。 I thought of writing a function to check a static URL and when we put an updated apk there, it would download it and install.我想写一个函数来检查静态 URL,当我们在那里放置更新的 apk 时,它会下载并安装。

I wrote this class (copying from another Stackoverflow question):我写了这个类(从另一个 Stackoverflow 问题复制):

class updateApp extends AsyncTask<String, Void, String> {
    protected String doInBackground(String... sUrl) {
        File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS),"updates");
        if(!file.mkdir()){
        }
        File f = new File(file.getAbsolutePath(),"YourApp.apk");
        Uri fUri = FileProvider.getUriForFile(MainActivity.this,"com.aktuna.vtv.monitor.fileprovider",f);
        String path = fUri.getPath();
        try {
            URL url = new URL(sUrl[0]);
            URLConnection connection = url.openConnection();
            connection.connect();
            InputStream input = new BufferedInputStream(url.openStream());
            OutputStream output = new FileOutputStream(path);

            byte data[] = new byte[1024];
            int count;
            while ((count = input.read(data)) != -1) {
                output.write(data, 0, count);
            }

            output.flush();
            output.close();
            input.close();
        } catch (Exception e) {
            Log.e("YourApp", "Well that didn't work out so well...");
            Log.e("YourApp", e.getMessage());
        }
        return path;
    }

    @Override
    protected void onPostExecute(String path) {
        Intent i = new Intent();
        i.setAction(Intent.ACTION_VIEW);
        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        File file = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS),"updates");
        File f = new File(file.getAbsolutePath(),"YourApp.apk");
        Uri fUri = FileProvider.getUriForFile(MainActivity.this,"com.aktuna.vtv.monitor.fileprovider",f);
        i.setDataAndType(fUri, "application/vnd.android.package-archive" );
        myCtx.startActivity(i);
    }
}

It seems to download the file successfully.好像下载成功了。 And then it sends the file in the intent to the installer (I can see this because the packageinstaller selection prompt comes) But then it does not install the new apk.然后它将意图中的文件发送到安装程序(我可以看到这一点,因为出现了 packageinstaller 选择提示)但是它不会安装新的 apk。

Since the previous Stackoverflow question is 7 years old, I thought that updating with no user interaction may be forbidden in new API levels.由于之前的 Stackoverflow 问题已有 7 年历史,因此我认为在新的 API 级别中可能会禁止在没有用户交互的情况下进行更新。

But I am not sure.但我不确定。 How can I troubleshoot this further ?我怎样才能进一步解决这个问题?

Also, I am open to any suggestions to achieve this, maybe something making use of older API levels or anything that would solve the "updating with no internet access through an internal static URL" issue.此外,我乐于接受任何实现这一目标的建议,也许是利用旧 API 级别或任何可以解决“无法通过内部静态 URL 访问 Internet 进行更新”问题的建议。

Thanks.谢谢。

I followed recommendation from @keag and it worked.我遵循了@keag 的建议,并且奏效了。

1. With no "root" on the device, I made the app "device-owner" For this I added a device admin receiver class. 1. 在设备上没有“root”的情况下,我将应用程序设为“设备所有者”为此我添加了一个设备管理接收器类。 SampleAdminReceiver.class: SampleAdminReceiver.class:

import android.app.admin.DeviceAdminReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class SampleAdminReceiver extends DeviceAdminReceiver {
    void showToast(Context context, CharSequence msg) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onEnabled(Context context, Intent intent) {
        showToast(context, "Device admin enabled");
    }
    @Override
    public void onDisabled(Context context, Intent intent) {
        showToast(context, "Device admin disabled");
    }
}

added receiver to the manifest:将接收器添加到清单中:

    <receiver
        android:name=".SampleAdminReceiver"
        android:description="@string/app_name"
        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_receiver" />
        <intent-filter>
            <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
        </intent-filter>
    </receiver>

Then using the adb interface I run the following dpm command:然后使用 adb 接口运行以下 dpm 命令:

$ dpm set-device-owner com.sample.app/.SampleAdminReceiver

Added following permission to manifest :添加了以下权限清单:

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

The with the following function I am able to install the apk from URL:具有以下功能,我可以从 URL 安装 apk:

public static boolean installPackageX(final Context context, final String url)
            throws IOException {
        //Use an async task to run the install package method
        AsyncTask<Void,Void,Void> task = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... voids) {
                try {
                    PackageInstaller packageInstaller = null;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        packageInstaller = context.getPackageManager().getPackageInstaller();
                    }
                    PackageInstaller.SessionParams params = null;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        params = new PackageInstaller.SessionParams(
                                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                    }

                    // set params
                    int sessionId = 0;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        sessionId = packageInstaller.createSession(params);
                    }
                    PackageInstaller.Session session = null;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        session = packageInstaller.openSession(sessionId);
                    }
                    OutputStream out = null;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        out = session.openWrite("COSU", 0, -1);
                    }
                    //get the input stream from the url
                    HttpURLConnection apkConn = (HttpURLConnection) new URL(url).openConnection();
                    InputStream in = apkConn.getInputStream();
                    byte[] buffer = new byte[65536];
                    int c;
                    while ((c = in.read(buffer)) != -1) {
                        out.write(buffer, 0, c);
                    }
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        session.fsync(out);
                    }
                    in.close();
                    out.close();
                    //you can replace this intent with whatever intent you want to be run when the applicaiton is finished installing
                    //I assume you have an activity called InstallComplete
                    Intent intent = new Intent(context, MainActivity.class);
                    intent.putExtra("info", "somedata");  // for extra data if needed..
                    Random generator = new Random();
                    PendingIntent i = PendingIntent.getActivity(context, generator.nextInt(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        session.commit(i.getIntentSender());
                    }
                } catch (Exception ex){
                    ex.printStackTrace();
                    Log.e("AppStore","Error when installing application. Error is " + ex.getMessage());
                }

                return null;
            }
        };
        task.execute(null,null);
        return true;
    }

After that, it is just a matter of automating the process.之后,这只是使过程自动化的问题。

Btw, following code in the app is useful for removing "device owner" property.顺便说一句,应用程序中的以下代码对于删除“设备所有者”属性很有用。

    DevicePolicyManager dpm = (DevicePolicyManager) getApplicationContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
    dpm.clearDeviceOwnerApp(getApplicationContext().getPackageName());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通过Java应用程序远程访问SQLite数据库 - Access SQLite datatabase remotely through java app 我在Google Play商店中的第一个应用程序向我展示了此内容 - My first app on google play store show me this 我的应用程序在三星Galaxy note 4的Google Play商店中不可见 - My app is not visible in google play store for samsung galaxy note 4 打开我的应用的Google Play页面,禁用导航/搜索功能 - Open Google Play page of my app, with navigation/search features disabled 为什么我不能在 Google Play 上发布我的应用程序 - Why Can't I Publish My App On Google Play 创建Android应用程序以远程控制我的计算机 - Creating android app to remotely control my computer 在Google Play商店中更新我的应用程序时,如何保持我的应用程序通知显示? - How can I keep my app's notification displayed when my app is updated on Google Play store? 无论如何测试谷歌应用内计费的订阅项目而不使他们的 state 在谷歌游戏控制台中处于活动状态? - Is there anyway to test subscription items of google in-app billing without making their state active in google play console? AOSP和对Google Play服务的访问 - AOSP and access to Google Play Services 如何从我的应用程序中打开 Google Play 商店应用程序页面? - How can I open a Google Play Store app page from within my app?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM