其他分享
首页 > 其他分享> > 远程升级(静默升级)

远程升级(静默升级)

作者:互联网

需求:公司有个广告投放的屏幕,需要在屏幕上进行广告播放,当app升级新东西的时候,对广告屏幕进行远程升级,这个是有root权限的,当时网上找了好多资料,然后结合自己整出来的这个,前面是工具类,最后面有使用方法和清单文件配置

public class AutoInstaller extends Handler {
    private static final String TAG = AutoInstaller.class.getSimpleName();
    private static final int REQUEST_CODE_PERMISSION_STORAGE = 100;
    private static volatile AutoInstaller mAutoInstaller;
    private Context mContext;
    private String mTempPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Download";

    public enum MODE {
        ROOT_ONLY,
        AUTO_ONLY,
        BOTH
    }

    private MODE mMode = MODE.BOTH;

    private AutoInstaller(Context context) {
        mContext = context;
    }

    public static AutoInstaller getDefault(Context context) {
        if (mAutoInstaller == null) {
            synchronized (AutoInstaller.class) {
                if (mAutoInstaller == null) {
                    mAutoInstaller = new AutoInstaller(context);
                }
            }
        }
        return mAutoInstaller;
    }

    public interface OnStateChangedListener {
        void onStart();

        void onComplete();

        void onNeed2OpenService();

        void needPermission();
    }

    private OnStateChangedListener mOnStateChangedListener;

    public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
        mOnStateChangedListener = onStateChangedListener;
    }

    public void execLinuxCommand(){
        String cmd = "sleep 5; am start -n \"com.example.advertputproject/com.example.advertputproject.MainActivity\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER";
        Runtime runtime = Runtime.getRuntime();
        OutputStream outputStream=null;
        try {
            Process su = runtime.exec("su");
            outputStream = su.getOutputStream();

            DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
            dataOutputStream.writeBytes(cmd);
            dataOutputStream.flush();
            Log.d("凉城aa", "设备准备重启");
        } catch (IOException pE) {
            pE.printStackTrace();
            Log.d("凉城aa", "failed"+pE.getMessage());
        }finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException pE) {
                    pE.printStackTrace();
                }
            }
        }
    }


//    public void autoRestart(Long delay){
//        Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(mContext.getPackageName());
//        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
//        AlarmManager systemService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
//        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
//            systemService.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+delay, pendingIntent);
//        }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//            systemService.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+delay, pendingIntent);
//        }
//    }

    private boolean installUseRoot(String filePath) {
        if (TextUtils.isEmpty(filePath))
            throw new IllegalArgumentException("Please check apk file path!");
        boolean result = false;
        Process process = null;
        OutputStream outputStream = null;
        BufferedReader errorStream = null;
        try {
            process = Runtime.getRuntime().exec("su");
            outputStream = process.getOutputStream();

            String command = "pm install -r " + filePath + "\n";     // pm install -r  覆盖安装已存在Apk,并保持原有数据;
            Log.d("凉城", command.toString());
            outputStream.write(command.getBytes());
            outputStream.flush();
            outputStream.write("exit\n".getBytes());
            outputStream.flush();
            process.waitFor();
            errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            StringBuilder msg = new StringBuilder();
            String line;
            while ((line = errorStream.readLine()) != null) {
                msg.append(line);
            }
            Log.d(TAG, "install msg is " + msg);
            if (!msg.toString().contains("Failure")) {
                result = true;
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage(), e);
            result = false;
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
                if (errorStream != null) {
                    errorStream.close();
                }
            } catch (IOException e) {
                outputStream = null;
                errorStream = null;
                process.destroy();
            }
        }
        return result;
    }

    private void installUseAS(String filePath) {
        // 存储空间
        if (permissionDenied()) {
            sendEmptyMessage(4);
            return;
        }

        // 允许安装应用
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            boolean b = mContext.getPackageManager().canRequestPackageInstalls();
            if (!b) {
                sendEmptyMessage(4);
                return;
            }
        }

        File file = new File(filePath);
        if (!file.exists()) {
            Log.e(TAG, "apk file not exists, path: " + filePath);
            return;
        }
        Uri uri = Uri.fromFile(file);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(mContext, "com.example.advertputproject.fileprovider", file);
            mContext.grantUriPermission(mContext.getPackageName(), contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        mContext.startActivity(intent);
        if (!isAccessibilitySettingsOn(mContext)) {
            toAccessibilityService();
            sendEmptyMessage(3);
        }
    }

    private boolean permissionDenied() {
        if (Build.VERSION.SDK_INT >= 23) {
            String[] permissions = {
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };

            for (String str : permissions) {
                if (mContext.checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) {
                    return true;
                }
            }
        }

        return false;
    }

    private void toAccessibilityService() {
        Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
        mContext.startActivity(intent);
    }


    private boolean isAccessibilitySettingsOn(Context mContext) {
        int accessibilityEnabled = 0;
        final String service = mContext.getPackageName() + "/" + InstallAccessibilityService.class.getCanonicalName();
        try {
            accessibilityEnabled = Settings.Secure.getInt(
                    mContext.getApplicationContext().getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_ENABLED);
            Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
        } catch (Settings.SettingNotFoundException e) {
            Log.e(TAG, "Error finding setting, default accessibility to not found: "
                    + e.getMessage());
        }
        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');

        if (accessibilityEnabled == 1) {
            Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------");
            String settingValue = Settings.Secure.getString(
                    mContext.getApplicationContext().getContentResolver(),
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                mStringColonSplitter.setString(settingValue);
                while (mStringColonSplitter.hasNext()) {
                    String accessibilityService = mStringColonSplitter.next();

                    Log.v(TAG, "-------------- > accessibilityService :: " + accessibilityService + " " + service);
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
                        return true;
                    }
                }
            }
        } else {
            Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
        }

        return false;
    }

    public void install(final String filePath) {
        if (TextUtils.isEmpty(filePath) || !filePath.endsWith(".apk"))
            throw new IllegalArgumentException("not a correct apk file path");
        new Thread(new Runnable() {
            @Override
            public void run() {
                sendEmptyMessage(1);
                switch (mMode) {
                    case BOTH:
                        if (!Utils.checkRooted() || !installUseRoot(filePath)) {
                            installUseAS(filePath);
                        }
                        break;
                    case ROOT_ONLY:
                        installUseRoot(filePath);
                        break;
                    case AUTO_ONLY:
                        installUseAS(filePath);
                }
                sendEmptyMessage(0);
            }
        }).start();
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 0:
                if (mOnStateChangedListener != null)
                    mOnStateChangedListener.onComplete();
                break;
            case 1:
                if (mOnStateChangedListener != null)
                    mOnStateChangedListener.onStart();
                break;

            case 3:
                if (mOnStateChangedListener != null)
                    mOnStateChangedListener.onNeed2OpenService();
                break;
            case 4:
                if (mOnStateChangedListener != null) {
                    mOnStateChangedListener.needPermission();
                }
                break;
        }
    }

    public void install(File file) {
        if (file == null)
            throw new IllegalArgumentException("file is null");
        install(file.getAbsolutePath());
    }

    public void installFromUrl(final String httpUrl) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                sendEmptyMessage(1);
                File file = downLoadFile(httpUrl);
             //   execLinuxCommand();
                install(file);
            }
        }).start();
    }

    private File downLoadFile(String httpUrl) {
        if (TextUtils.isEmpty(httpUrl)) throw new IllegalArgumentException();
        String path = mContext.getFilesDir().getAbsolutePath();
        File file = new File(path);

        Log.d(TAG, "app路径:" + path);

        if (!file.exists()) file.mkdirs();
        file = new File(path + File.separator + "update.apk");
        InputStream inputStream = null;
        FileOutputStream outputStream = null;
        HttpURLConnection connection = null;
        try {
            URL url = new URL(httpUrl);
            connection = (HttpURLConnection) url.openConnection();
            if (connection instanceof HttpsURLConnection) {
                SSLContext sslContext = getSLLContext();
                if (sslContext != null) {
                    SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
                    ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
                }
            }
            connection.setConnectTimeout(60 * 1000);
            connection.setReadTimeout(60 * 1000);
            connection.connect();
            inputStream = connection.getInputStream();
            outputStream = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null)
                    inputStream.close();
                if (outputStream != null)
                    outputStream.close();
                if (connection != null)
                    connection.disconnect();
            } catch (IOException e) {
                inputStream = null;
                outputStream = null;
            }
        }
        return file;
    }

    private SSLContext getSLLContext() {
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }}, new SecureRandom());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslContext;
    }

//    public static class Builder {
//
//        private MODE mode = MODE.BOTH;
//        private Context context;
//        private OnStateChangedListener onStateChangedListener;
//        private String directory = Environment.getExternalStorageDirectory().getAbsolutePath();
//
//        public Builder(Context c) {
//            context = c;
//        }
//
//        public Builder setMode(MODE m) {
//            mode = m;
//            return this;
//        }
//
//        public Builder setOnStateChangedListener(OnStateChangedListener o) {
//            onStateChangedListener = o;
//            return this;
//        }
//
//        public Builder setCacheDirectory(String path) {
//            directory = path;
//            return this;
//        }
//
//        public AutoInstaller build() {
//            AutoInstaller autoInstaller = new AutoInstaller(context);
//            autoInstaller.mMode = mode;
//            autoInstaller.mOnStateChangedListener = onStateChangedListener;
//            autoInstaller.mTempPath = directory;
//            return autoInstaller;
//        }
//
//    }
}
public class InstallAccessibilityService extends AccessibilityService {

    private static final String TAG = InstallAccessibilityService.class.getSimpleName();

    private Map<Integer, Boolean> handledMap = new HashMap<>();

    private Handler mHandler = new Handler(Looper.getMainLooper());

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.d(TAG, "onAccessibilityEvent: " + event.toString());

        if (!String.valueOf(event.getPackageName()).contains("packageinstaller")) {
            //不写完整包名,是因为某些手机(如小米)安装器包名是自定义的
            return;
        }

        AccessibilityNodeInfo nodeInfo = event.getSource();
        if (nodeInfo == null) {
            Log.i(TAG, "eventNode: null, 重新获取eventNode...");
            performGlobalAction(GLOBAL_ACTION_RECENTS); // 打开最近页面
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    performGlobalAction(GLOBAL_ACTION_BACK); // 返回安装页面
                }
            }, 320);
            return;
        }

        int eventType = event.getEventType();
        if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED ||
                eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            if (handledMap.get(event.getWindowId()) == null) {
                boolean handled = iterateNodesAndHandle(nodeInfo);
                if (handled) {
                    handledMap.put(event.getWindowId(), true);
                }
            }
        }
    }

    private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) {
        if (nodeInfo != null) {
            int childCount = nodeInfo.getChildCount();
            if ("android.widget.Button".equals(nodeInfo.getClassName())) {
                String nodeContent = nodeInfo.getText().toString();
                Log.d("TAG", "content is " + nodeContent);
                if (!TextUtils.isEmpty(nodeContent)
                        && ("安装".equals(nodeContent)
                        || "install".equals(nodeContent.toLowerCase())
                        || "done".equals(nodeContent.toLowerCase())
                        || "完成".equals(nodeContent)
                        || "确定".equals(nodeContent)
                )) {
                    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    return true;
                }
            } else if ("android.widget.ScrollView".equals(nodeInfo.getClassName())) {
                nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
            }
            for (int i = 0; i < childCount; i++) {
                AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
                if (iterateNodesAndHandle(childNodeInfo)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void onInterrupt() {

    }
}
public class Utils {

    public static final String TAG = "Utils";

    // 此方法工作有误
    @Deprecated
    public static boolean isRooted() {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("su");
            OutputStream outputStream = process.getOutputStream();
            InputStream inputStream = process.getInputStream();
            outputStream.write("id\n".getBytes());
            outputStream.flush();
            outputStream.write("exit\n".getBytes());
            outputStream.flush();
            process.waitFor();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s = bufferedReader.readLine();
            if (s.contains("uid=0")) return true;
        } catch (IOException e) {
            Log.e(TAG, "没有root权限");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (process != null)
                process.destroy();
        }
        return false;
    }

    public static boolean checkRooted() {
        boolean result = false;
        try {
            result = new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

//应用

public class MainActivity extends AppCompatActivity {

    private Banner banner;
    public static final String APK_FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Download" + File.separator + "app-release.apk";
    public static final String CACHE_FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "Download";
    public static final String APK_URL = "https://。。。。。/apk/app-release.apk";  //apk下载地址

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //沉浸式状态栏
        ImmersionModeUtil.setStatusBar(this, false);

        Log.d("凉城", "onCreate");

        if (Build.VERSION.SDK_INT >= 23) {
            int REQUEST_CODE_CONTACT = 101;
            String[] permissions = {
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE};
            //验证是否许可权限
            for (String str : permissions) {
                if (checkSelfPermission(str) != PackageManager.PERMISSION_GRANTED) {
                    //申请权限
                    requestPermissions(permissions, REQUEST_CODE_CONTACT);
                    initUpdate();
                    initView();
                    initData();
                    return;
                } else {
                    //这里就是权限打开之后自己要操作的逻辑
                    initUpdate();
                    initView();
                    initData();
                    return;
                }
            }
        }


    }

    private void initUpdate() {
        String versionName = getAppVersionName(this);
        Log.d("凉城versionName", versionName);

        String url = "/data/user/0/com.example.advertputproject/files/update.apk";  ///下载后apk存在的位置,

		//获取下载后apk的版本号
        String apkVersionName = "";
        PackageManager manager = getPackageManager();
        PackageInfo info = manager.getPackageArchiveInfo(url, PackageManager.GET_ACTIVITIES);
        if(info != null){
            ApplicationInfo applicationInfo = info.applicationInfo;
            applicationInfo.sourceDir = url;
            applicationInfo.publicSourceDir = url;
            String appName = manager.getApplicationLabel(applicationInfo).toString();// 得到应用名
            String packageName = applicationInfo.packageName;// 得到包名
            apkVersionName = info.versionName; // 得到版本信息

            Log.d("凉城", "apkInfo----appName "+appName);
            Log.d("凉城", "apkInfo----packageName "+packageName);
            Log.d("凉城", "apkInfo----apkVersionName "+apkVersionName);
        }

		//如果下载的版本号和app的版本号一样就不升级
        if(apkVersionName.equals(versionName)){
            return;
        }else {
            AutoInstaller installer = AutoInstaller.getDefault(MainActivity.this);
            installer.installFromUrl(APK_URL);
            installer.setOnStateChangedListener(new AutoInstaller.OnStateChangedListener() {
                @Override
                public void onStart() {
                }

                @Override
                public void onComplete() {
                }

                @Override
                public void onNeed2OpenService() {
                    Toast.makeText(MainActivity.this, "请打开辅助功能服务", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void needPermission() {
                    Toast.makeText(MainActivity.this, "需要申请存储空间权限", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    private void initData() {
        Retrofit build = new Retrofit.Builder()
                .baseUrl(ApiService.baseUrl)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService apiService = build.create(ApiService.class);
        Observable<BannerBean> imageUrl = apiService.getImageUrl();
        imageUrl.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<BannerBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(BannerBean pBannerBean) {
                        List<BannerBean.DataBean> data = pBannerBean.getData();
                        banner.setImages(data).setImageLoader(new ImageLoader() {
                            @Override
                            public void displayImage(Context context, Object path, ImageView imageView) {
                                BannerBean.DataBean bean = (BannerBean.DataBean) path;
                                Glide.with(MainActivity.this).load(bean.getImagePath()).into(imageView);
                            }
                        }).start();
                    }

                    @Override
                    public void one rror(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    private void initView() {
        banner = (Banner) findViewById(R.id.banner);
    }

升级完后发广播进行通知,重新启动app

public class AppInstallReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
            Toast.makeText(context, "安装成功", Toast.LENGTH_LONG).show();
            AutoInstaller.getDefault(context).execLinuxCommand();
        }
        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
            Toast.makeText(context, "卸载成功", Toast.LENGTH_LONG).show();
        }
        if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
            Toast.makeText(context, "替换成功", Toast.LENGTH_LONG).show();
            AutoInstaller.getDefault(context).execLinuxCommand();//升级完后重新启动app
        }

        Log.d("凉城升级", "onReceive");
    }
}

清单文件配置

<receiver
            android:name=".AppInstallReceiver"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />

                <data android:scheme="package" />
            </intent-filter>
        </receiver>
        <service
            android:name=".util.InstallAccessibilityService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config" />
        </service>
        <!-- 文件访问 -->
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.advertputproject.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

//file_paths

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="download"
        path="Download" />

    <external-path
        name="ext_root"
        path="/" />

    <external-path
        name="external_storage_root"
        path="." />
    <external-path
        name="files_root"
        path="Android/data/com.example.advertputproject/" />
</paths>

//accessibility_service_config

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_description" />

标签:String,void,private,public,升级,静默,new,null,远程
来源: https://blog.csdn.net/qq_46237697/article/details/120265982