admin 管理员组

文章数量: 1184232

在android 9.0自动下载更新时 遇到 安装包解析错误

12-1819:21:32.66548045604 W InstallStaging: java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.***.***.update_app.file_provider/download/update/***.apk from pid=4804, uid=1000 requires the provider be exported, or grantUriPermission()
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.os.Parcel.createException(Parcel.java:1950)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.os.Parcel.readException(Parcel.java:1918)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:698)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1459)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1296)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.content.ContentResolver.openInputStream(ContentResolver.java:1016)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:167)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:160)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.os.AsyncTask$2.call(AsyncTask.java:333)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
12-18 19:21:32.665  4804  5604 W InstallStaging: 	at java.lang.Thread.run(Thread.java:764)

发现Android9.0是需要权限读取fileProvider和uri的
于是我看了一下三方框架的源码,确实没有加权限

private Intent installIntent(Context context, String path){
        Intent intent =newIntent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N&&null!= fileProviderAuthority){
            Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
            intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);}else{
            intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}

于是我就扒了一下源码对其进行修改

private Intent installIntent(Context context, String path){
        Intent intent =newIntent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N&&null!= fileProviderAuthority){
            Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
            intent.setDataAndType(fileUri,"application/vnd.android.package-archive");grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}else{
            intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}

测试通过,完美安装,没有出现解析失败问题

代码:

<!--AndroidManifest.xml--><uses-permission android:name="android.permission.FOREGROUND_SERVICE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.INTERNET"/><provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="包名.file_provider"
            android:exported="false"
            android:grantUriPermissions="true"><!-- 元数据 --><meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                tools:replace="android:resource"/></provider><service android:name=".UpdateService"/>//从源码下载的升级下载安装apk的服务
<!-- file_paths.xml --><?xml version="1.0" encoding="utf-8"?><paths><external-path path="." name="download"/><external-cache-path
        name="storage/emulated/0"
        path="."/></paths>
/**
 * Created by Sun on 2020/12/19.
 */publicclassUpdateServiceextendsService{publicstaticfinal String TAG ="UpdateService";publicstaticfinal String ACTION ="me.shenfan.UPDATE_APP";publicstaticfinal String STATUS ="status";publicstaticfinal String PROGRESS ="progress";publicstaticboolean DEBUG =false;//下载大小通知频率publicstaticfinalint UPDATE_NUMBER_SIZE =1;publicstaticfinalint DEFAULT_RES_ID =-1;publicstaticfinalint UPDATE_PROGRESS_STATUS =0;publicstaticfinalint UPDATE_ERROR_STATUS =-1;publicstaticfinalint UPDATE_SUCCESS_STATUS =1;//paramsprivatestaticfinal String URL ="downloadUrl";privatestaticfinal String ICO_RES_ID ="icoResId";privatestaticfinal String ICO_SMALL_RES_ID ="icoSmallResId";privatestaticfinal String UPDATE_PROGRESS ="updateProgress";privatestaticfinal String STORE_DIR ="storeDir";privatestaticfinal String DOWNLOAD_NOTIFICATION_FLAG ="downloadNotificationFlag";privatestaticfinal String DOWNLOAD_SUCCESS_NOTIFICATION_FLAG ="downloadSuccessNotificationFlag";privatestaticfinal String DOWNLOAD_ERROR_NOTIFICATION_FLAG ="downloadErrorNotificationFlag";privatestaticfinal String IS_SEND_BROADCAST ="isSendBroadcast";private String downloadUrl;privateint icoResId;//default app icoprivateint icoSmallResId;privateint updateProgress;//update notification progress when it add numberprivate String storeDir;//default sdcard/Android/package/updateprivateint downloadNotificationFlag;privateint downloadSuccessNotificationFlag;privateint downloadErrorNotificationFlag;privateboolean isSendBroadcast;private UpdateProgressListener updateProgressListener;private LocalBinder localBinder =newLocalBinder();/**
     * Class used for the client Binder.
     */publicclassLocalBinderextendsBinder{/**
         * set update progress call back
         *
         * @param listener
         */publicvoidsetUpdateProgressListener(UpdateProgressListener listener){
            UpdateService.this.setUpdateProgressListener(listener);}}privateboolean startDownload;//开始下载privateint lastProgressNumber;private NotificationCompat.Builder builder;private NotificationManager manager;privateint notifyId;private String appName;private LocalBroadcastManager localBroadcastManager;private Intent localIntent;private DownloadApk downloadApkTask;/**
     * whether debug
     */publicstaticvoiddebug(){
        DEBUG =true;}private Intent installIntent(Context context, String path){
        Intent intent =newIntent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && null!= fileProviderAuthority){
            Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
            intent.setDataAndType(fileUri,"application/vnd.android.package-archive");grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}else{
            intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}/**
     * 获取FileProvider的auth
     */privatestatic String getFileProviderAuthority(Context context){try{for(ProviderInfo provider : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS).providers){if(FileProvider.class.getName().equals(provider.name)&& provider.authority.endsWith(".update_app.file_provider")){return provider.authority;}}}catch(PackageManager.NameNotFoundException ignore){}return null;}privatestatic Intent webLauncher(String downloadUrl){
        Uri download = Uri.parse(downloadUrl);
        Intent intent =newIntent(Intent.ACTION_VIEW, download);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);return intent;}privatestatic String getSaveFileName(String downloadUrl){if(downloadUrl == null || TextUtils.isEmpty(downloadUrl)){return System.currentTimeMillis()+".apk";}return downloadUrl.substring(downloadUrl.lastIndexOf("/"));}privatestatic File getDownloadDir(UpdateService service){
        File downloadDir = null;if(Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)){if(service.storeDir != null){
                downloadDir =newFile(Environment.getExternalStorageDirectory(), service.storeDir);}else{
                downloadDir =newFile(service.getExternalCacheDir(),"update");}}else{
            downloadDir =newFile(service.getCacheDir(),"update");}if(!downloadDir.exists()){
            downloadDir.mkdirs();}return downloadDir;}@OverridepublicvoidonCreate(){super.onCreate();
        appName =getApplicationName();}@OverridepublicintonStartCommand(Intent intent,int flags,int startId){if(!startDownload && intent != null){
            startDownload =true;
            downloadUrl = intent.getStringExtra(URL);
            icoResId = intent.getIntExtra(ICO_RES_ID, DEFAULT_RES_ID);
            icoSmallResId = intent.getIntExtra(ICO_SMALL_RES_ID, DEFAULT_RES_ID);
            storeDir = intent.getStringExtra(STORE_DIR);
            updateProgress = intent.getIntExtra(UPDATE_PROGRESS, UPDATE_NUMBER_SIZE);
            downloadNotificationFlag = intent.getIntExtra(DOWNLOAD_NOTIFICATION_FLAG,0);
            downloadErrorNotificationFlag = intent.getIntExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG,0);
            downloadSuccessNotificationFlag = intent.getIntExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG,0);
            isSendBroadcast = intent.getBooleanExtra(IS_SEND_BROADCAST,false);if(DEBUG){
                Log.d(TAG,"downloadUrl: "+ downloadUrl);
                Log.d(TAG,"icoResId: "+ icoResId);
                Log.d(TAG,"icoSmallResId: "+ icoSmallResId);
                Log.d(TAG,"storeDir: "+ storeDir);
                Log.d(TAG,"updateProgress: "+ updateProgress);
                Log.d(TAG,"downloadNotificationFlag: "+ downloadNotificationFlag);
                Log.d(TAG,"downloadErrorNotificationFlag: "+ downloadErrorNotificationFlag);
                Log.d(TAG,"downloadSuccessNotificationFlag: "+ downloadSuccessNotificationFlag);
                Log.d(TAG,"isSendBroadcast: "+ isSendBroadcast);}
            notifyId = startId;buildNotification();buildBroadcast();
            downloadApkTask =newDownloadApk(this);
            downloadApkTask.execute(downloadUrl);}returnsuper.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent){return localBinder;}@OverridepublicbooleanonUnbind(Intent intent){returntrue;}publicvoidsetUpdateProgressListener(UpdateProgressListener updateProgressListener){this.updateProgressListener = updateProgressListener;}@OverridepublicvoidonDestroy(){if(downloadApkTask != null){
            downloadApkTask.cancel(true);}if(updateProgressListener != null){
            updateProgressListener = null;}
        localIntent = null;
        builder = null;super.onDestroy();}public String getApplicationName(){
        PackageManager packageManager = null;
        ApplicationInfo applicationInfo = null;try{
            packageManager =getApplicationContext().getPackageManager();
            applicationInfo = packageManager.getApplicationInfo(getPackageName(),0);}catch(PackageManager.NameNotFoundException e){
            applicationInfo = null;}
        String applicationName =(String) packageManager.getApplicationLabel(applicationInfo);return applicationName;}privatevoidbuildBroadcast(){if(!isSendBroadcast){return;}
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localIntent =newIntent(ACTION);}privatevoidsendLocalBroadcast(int status,int progress){if(!isSendBroadcast || localIntent == null){return;}
        localIntent.putExtra(STATUS, status);
        localIntent.putExtra(PROGRESS, progress);
        localBroadcastManager.sendBroadcast(localIntent);}privatevoidbuildNotification(){
        manager =(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        builder =newNotificationCompat.Builder(this);
        builder.setContentTitle(getString(R.string.update_app_model_prepare, appName)).setWhen(System.currentTimeMillis()).setProgress(100,1,false).setSmallIcon(icoSmallResId).setLargeIcon(BitmapFactory.decodeResource(getResources(), icoResId)).setDefaults(downloadNotificationFlag);
        manager.notify(notifyId, builder.build());}privatevoidstart(){
        builder.setContentTitle(appName);
        builder.setContentText(getString(R.string.update_app_model_progress,1,"%"));//setChannelId 必须添加否则不能在通知栏显示(Android 8.0)
        builder.setChannelId(getPackageName());
        manager.notify(notifyId, builder.build());sendLocalBroadcast(UPDATE_PROGRESS_STATUS,1);if(updateProgressListener != null){
            updateProgressListener.start();}}/**
     * @param progress download percent , max 100
     */privatevoidupdate(int progress){if(progress - lastProgressNumber > updateProgress){
            lastProgressNumber = progress;
            builder.setProgress(100, progress,false);
            builder.setContentText(getString(R.string.update_app_model_progress, progress,"%"));
            manager.notify(notifyId, builder.build());sendLocalBroadcast(UPDATE_PROGRESS_STATUS, progress);if(updateProgressListener != null){
                updateProgressListener.update(progress);}}}privatevoidsuccess(String path){
        builder.setProgress(0,0,false);
        builder.setContentText(getString(R.string.update_app_model_success));
        Intent i =installIntent(this, path);
        PendingIntent intent = PendingIntent.getActivity(this,0, i, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(intent);
        builder.setDefaults(downloadSuccessNotificationFlag);
        Notification n = builder.build();
        n.contentIntent = intent;
        manager.notify(notifyId, n);sendLocalBroadcast(UPDATE_SUCCESS_STATUS,100);if(updateProgressListener != null){
            updateProgressListener.success();}startActivity(i);stopSelf();}privatevoiderror(){
        Intent i =webLauncher(downloadUrl);
        PendingIntent intent = PendingIntent.getActivity(this,0, i,
                PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentText(getString(R.string.update_app_model_error));
        builder.setContentIntent(intent);
        builder.setProgress(0,0,false);
        builder.setDefaults(downloadErrorNotificationFlag);
        Notification n = builder.build();
        n.contentIntent = intent;
        manager.notify(notifyId, n);sendLocalBroadcast(UPDATE_ERROR_STATUS,-1);if(updateProgressListener != null){
            updateProgressListener.error();}stopSelf();}privatestaticclassDownloadApkextendsAsyncTask<String, Integer, String>{private WeakReference<UpdateService> updateServiceWeakReference;publicDownloadApk(UpdateService service){
            updateServiceWeakReference =newWeakReference<>(service);}@OverrideprotectedvoidonPreExecute(){super.onPreExecute();
            UpdateService service = updateServiceWeakReference.get();if(service != null){
                service.start();}}@Overrideprotected String doInBackground(String... params){final String downloadUrl = params[0];final File file =newFile(UpdateService.getDownloadDir(updateServiceWeakReference.get()),
                    UpdateService.getSaveFileName(downloadUrl));if(DEBUG){
                Log.d(TAG,"download url is "+ downloadUrl);
                Log.d(TAG,"download apk cache at "+ file.getAbsolutePath());}
            File dir = file.getParentFile();if(!dir.exists()){
                dir.mkdirs();}
            HttpURLConnection httpConnection = null;
            InputStream is = null;
            FileOutputStream fos = null;int updateTotalSize =0;
            URL url;try{
                url =newURL(downloadUrl);
                httpConnection =(HttpURLConnection) url.openConnection();
                httpConnection.setConnectTimeout(20000);
                httpConnection.setReadTimeout(20000);if(DEBUG){
                    Log.d(TAG,"download status code: "+ httpConnection.getResponseCode());}if(httpConnection.getResponseCode()!=200){return null;}
                updateTotalSize = httpConnection.getContentLength();if(file.exists()){if(updateTotalSize == file.length()){// 下载完成return file.getAbsolutePath();}else{
                        file.delete();}}
                file.createNewFile();
                is = httpConnection.getInputStream();
                fos =newFileOutputStream(file,false);byte buffer[]=newbyte[4096];int readSize =0;int currentSize =0;while((readSize = is.read(buffer))>0){
                    fos.write(buffer,0, readSize);
                    currentSize += readSize;publishProgress((currentSize *100/ updateTotalSize));}// download success}catch(Exception e){
                e.printStackTrace();return null;}finally{if(httpConnection != null){
                    httpConnection.disconnect();}if(is != null){try{
                        is.close();}catch(IOException e){
                        e.printStackTrace();}}if(fos != null){try{
                        fos.close();}catch(IOException e){
                        e.printStackTrace();}}}return file.getAbsolutePath();}@OverrideprotectedvoidonProgressUpdate(Integer... values){super.onProgressUpdate(values);if(DEBUG){
                Log.d(TAG,"current progress is "+ values[0]);}
            UpdateService service = updateServiceWeakReference.get();if(service != null){
                service.update(values[0]);}}@OverrideprotectedvoidonPostExecute(String s){super.onPostExecute(s);
            UpdateService service = updateServiceWeakReference.get();if(service != null){if(s != null){
                    service.success(s);}else{
                    service.error();}}}}/**
     * a builder class helper use UpdateService
     */publicstaticclassBuilder{private String downloadUrl;privateint icoResId = DEFAULT_RES_ID;//default app icoprivateint icoSmallResId = DEFAULT_RES_ID;privateint updateProgress = UPDATE_NUMBER_SIZE;//update notification progress when it add numberprivate String storeDir;//default sdcard/Android/package/updateprivateint downloadNotificationFlag;privateint downloadSuccessNotificationFlag;privateint downloadErrorNotificationFlag;privateboolean isSendBroadcast;protectedBuilder(String downloadUrl){this.downloadUrl = downloadUrl;}publicstatic Builder create(String downloadUrl){if(downloadUrl == null){thrownewNullPointerException("downloadUrl == null");}returnnewBuilder(downloadUrl);}public String getDownloadUrl(){return downloadUrl;}publicintgetIcoResId(){return icoResId;}public Builder setIcoResId(int icoResId){this.icoResId = icoResId;returnthis;}publicintgetIcoSmallResId(){return icoSmallResId;}public Builder setIcoSmallResId(int icoSmallResId){this.icoSmallResId = icoSmallResId;returnthis;}publicintgetUpdateProgress(){return updateProgress;}public Builder setUpdateProgress(int updateProgress){if(updateProgress <1){thrownewIllegalArgumentException("updateProgress < 1");}this.updateProgress = updateProgress;returnthis;}public String getStoreDir(){return storeDir;}public Builder setStoreDir(String storeDir){this.storeDir = storeDir;returnthis;}publicintgetDownloadNotificationFlag(){return downloadNotificationFlag;}public Builder setDownloadNotificationFlag(int downloadNotificationFlag){this.downloadNotificationFlag = downloadNotificationFlag;returnthis;}publicintgetDownloadSuccessNotificationFlag(){return downloadSuccessNotificationFlag;}public Builder setDownloadSuccessNotificationFlag(int downloadSuccessNotificationFlag){this.downloadSuccessNotificationFlag = downloadSuccessNotificationFlag;returnthis;}publicintgetDownloadErrorNotificationFlag(){return downloadErrorNotificationFlag;}public Builder setDownloadErrorNotificationFlag(int downloadErrorNotificationFlag){this.downloadErrorNotificationFlag = downloadErrorNotificationFlag;returnthis;}publicbooleanisSendBroadcast(){return isSendBroadcast;}public Builder setIsSendBroadcast(boolean isSendBroadcast){this.isSendBroadcast = isSendBroadcast;returnthis;}public Builder build(Context context){if(context == null){thrownewNullPointerException("context == null");}
            Intent intent =newIntent();
            intent.setClass(context, UpdateService.class);
            intent.putExtra(URL, downloadUrl);if(icoResId == DEFAULT_RES_ID){
                icoResId =getIcon(context);}if(icoSmallResId == DEFAULT_RES_ID){
                icoSmallResId = icoResId;}
            intent.putExtra(ICO_RES_ID, icoResId);
            intent.putExtra(STORE_DIR, storeDir);
            intent.putExtra(ICO_SMALL_RES_ID, icoSmallResId);
            intent.putExtra(UPDATE_PROGRESS, updateProgress);
            intent.putExtra(DOWNLOAD_NOTIFICATION_FLAG, downloadNotificationFlag);
            intent.putExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG, downloadSuccessNotificationFlag);
            intent.putExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG, downloadErrorNotificationFlag);
            intent.putExtra(IS_SEND_BROADCAST, isSendBroadcast);
            context.startService(intent);returnthis;}privateintgetIcon(Context context){final PackageManager packageManager = context.getPackageManager();
            ApplicationInfo appInfo = null;try{
                appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);}catch(PackageManager.NameNotFoundException e){
                e.printStackTrace();}if(appInfo != null){return appInfo.icon;}return0;}}}
/**
 * 下载回调
 */publicinterfaceUpdateProgressListener{/**
     * download start
     */voidstart();/**
     * update download progress
     * @param progress
     */voidupdate(int progress);/**
     * download success
     */voidsuccess();/**
     * download error
     */voiderror();}
			UpdateService.Builder.create("服务器给的下载路径").setStoreDir("下载路径")//default sdcard/Android/package/update.setIsSendBroadcast(true).setIcoResId(android.R.drawable.ic_notification_clear_all).setIcoSmallResId(android.R.drawable.ic_notification_overlay).build(context);

本文标签: 遇到在 系统 新时